Back to index

python-weblib  1.3.9
forms.py
Go to the documentation of this file.
00001 """
00002 pyweblib.forms - class library for handling <FORM> input
00003 (c) by Michael Stroeder <michael@stroeder.com>
00004 
00005 This module is distributed under the terms of the
00006 GPL (GNU GENERAL PUBLIC LICENSE) Version 2
00007 (see http://www.gnu.org/copyleft/gpl.html)
00008 
00009 Python compability note:
00010 This module only works with Python 2.0 since all string parameters
00011 are assumed to be Unicode objects and string methods are used instead
00012 string module.
00013 
00014 $Id: forms.py,v 1.47 2012/03/16 13:09:48 michael Exp $
00015 """
00016 
00017 __version__ = '0.16.0'
00018 
00019 import sys,types,re,urllib
00020 import helper
00021 
00022 def escapeHTML(s):
00023   """
00024   Escape all characters with a special meaning in HTML
00025   to appropriate character tags
00026   """
00027   s = s.replace('&','&#38;')
00028   s = s.replace('<','&#60;')
00029   s = s.replace('>','&#62;')
00030   s = s.replace("'",'&#39;')
00031   s = s.replace('"','&#34;')
00032   s = s.replace(':','&#58;')
00033   s = s.replace('=','&#61;')
00034   s = s.replace('{','&#123;')
00035   s = s.replace('}','&#125;')
00036   s = s.replace('(','&#40;')
00037   s = s.replace(')','&#41;')
00038   s = s.replace('`','&#96;')
00039   return s
00040 
00041 
00042 class Field:
00043   """
00044   Base class for all kind of single input fields.
00045 
00046   In most cases this class is not used directly
00047   since derivate classes for most types of input fields exist.
00048   """
00049 
00050   def __init__(
00051     self,name,text,maxLen,maxValues,pattern,required=0,default=None,accessKey='',
00052   ):
00053     """
00054     name
00055         Field name used in <INPUT NAME="..">
00056     text
00057         User-friendly text describing this field
00058     maxLen
00059         maximum length of a single input value [Bytes]
00060     maxValues
00061         maximum amount of input values
00062     default
00063         default value to be used in method inputfield()
00064     required
00065         flag which marks field as mandantory input field
00066     accessKey
00067         key for accessing this field to be displayed by method inputHTML()
00068     pattern
00069         regex pattern of valid values either as string
00070         or tuple (pattern,options)
00071     """
00072     self.value = []
00073     self.name = name
00074     self.text = text
00075     self.maxLen = maxLen
00076     self.maxValues = maxValues
00077     self.required = required
00078     self.accessKey = accessKey
00079     self.inputHTMLTemplate = r'%s'
00080     self.valueHTMLTemplate = r'%s'
00081     # Charset is the preferred character set of the browser.
00082     # This is set by Form.add() the something meaningful.
00083     self.charset = 'iso-8859-1'
00084     self.setDefault(default)
00085     self.setRegex(pattern)
00086 
00087   def _accessKeyAttr(self):
00088     if self.accessKey:
00089       return 'accesskey="%s"' % (self.accessKey)
00090     else:
00091       return ''
00092 
00093   def idAttrStr(self,id_value):
00094     if id_value is None:
00095       return ''
00096     else:
00097       return 'id="%s" ' % (id_value)
00098 
00099   def labelHTML(self,labelText=None,for_value=None):
00100     labelText = (labelText or self.text).encode(self.charset)
00101     return '<label for="%s">%s</label>' % (for_value or self.name,labelText)
00102 
00103   def getValue(self):
00104     """
00105     Returns self.value in case of multi-valued input or
00106     self.value[0] if only one value is allowed.
00107     """
00108     if self.maxValues>1:
00109       return self.value
00110     else:
00111       return self.value[0]
00112 
00113   def setDefault(self,default):
00114     """
00115     Set the default of a field.
00116 
00117     Mainly this is used by the application if self.default shall
00118     be changed after initializing the field object.
00119     """
00120     if type(default)==types.ListType:
00121       self.default = [i for i in default if i!=None]
00122     else:
00123       self.default = default or ''
00124 
00125   def _patternAndOptions(self,pattern):
00126     """
00127     The result is a tuple (pattern string,pattern options).
00128 
00129     pattern
00130         Either a string containing a regex pattern,
00131         a tuple (pattern string, pattern options) or None.
00132     """
00133     if type(pattern) is types.TupleType:
00134       return pattern
00135     else:
00136       return pattern, 0
00137 
00138   def setRegex(self,pattern):
00139     """
00140     Set the regex pattern for validating this field.
00141 
00142     Mainly this is used if self._re shall be changed after
00143     the field object was initialized.
00144 
00145     pattern
00146         Either a string containing a regex pattern,
00147         a tuple (pattern string, pattern options) or None.
00148         If None regex checking in _validateFormat is switched off
00149         (not recommended).
00150     """
00151     patternstring,patternoptions = self._patternAndOptions(pattern)
00152     if patternstring is None:
00153       # Regex checking is completely switched off
00154       self._re = None
00155     else:
00156       # This is a Unicode input field
00157       patternoptions = patternoptions | re.U
00158       self._re = re.compile('%s$' % patternstring,patternoptions)
00159 
00160   def _validateLen(self,value):
00161     """Check length of the user's value for this field."""
00162     if len(value)>self.maxLen:
00163       raise InvalidValueLen(self.name,self.text,len(value),self.maxLen)
00164 
00165   def _validateFormat(self,value):
00166     """
00167     Check format of the user's value for this field.
00168 
00169     Empty input (zero-length string) are valid in any case.
00170     You might override this method to change this behaviour.
00171     """
00172     if (not self._re is None) and value:
00173       rm = self._re.match(value)
00174       if rm is None:
00175         raise InvalidValueFormat(
00176           self.name,
00177           self.text.encode(self.charset),
00178           value.encode(self.charset)
00179         )
00180 
00181   def _validateMaxValue(self):
00182     if len(self.value)>=self.maxValues:
00183       raise TooManyValues(self.name,self.text,len(self.value),self.maxValues)
00184 
00185   def _encodeValue(self,value):
00186     """
00187     Return Unicode object or string to be stored in self.value
00188     """
00189     try:
00190       value = unicode(value,self.charset)
00191     except UnicodeError:
00192       # Work around buggy browsers...
00193       value = unicode(value,'iso-8859-1')
00194     return value
00195 
00196   def setValue(self,value):
00197     """
00198     Store the user's value into the field object.
00199 
00200     This method can be used to modify the user's value
00201     before storing it into self.value.
00202     """
00203     value = self._encodeValue(value)
00204     # Check if input is valid
00205     # Length valid?
00206     self._validateLen(value)
00207     # Format valid?
00208     self._validateFormat(value)
00209     self._validateMaxValue()
00210     self.value.append(value)
00211 
00212   def setCharset(self,charset):
00213     """Define the character set of the user's input."""
00214     self.charset = charset
00215 
00216   def _defaultValue(self,default):
00217     """returns default value"""
00218     return default or self.__dict__.get('default','')
00219 
00220   def titleHTML(self,title):
00221     """HTML output of default."""
00222     return escapeHTML(title or self.text).encode(self.charset)
00223 
00224   def _defaultHTML(self,default):
00225     """HTML output of default."""
00226     return escapeHTML(self._defaultValue(default)).encode(self.charset)
00227 
00228   def valueHTML(self):
00229     """
00230     HTML output of self.value using the HTML template string
00231     in self.valueHTMLTemplate.
00232     """
00233     return [
00234       self.valueHTMLTemplate % v
00235       for v in self.value
00236     ]
00237 
00238 
00239 class Textarea(Field):
00240   """
00241   Multi-line input field:
00242   <textarea>
00243   """
00244 
00245   def __init__(
00246     self,name,text,maxLen,maxValues,pattern,required=0,default=None,accessKey='',
00247     rows=10,cols=60
00248   ):
00249     self.rows  = rows
00250     self.cols  = cols
00251     Field.__init__(
00252       self,name,text,maxLen,maxValues,None,required,default,accessKey
00253     )
00254 
00255   def setRegex(self,pattern):
00256     """
00257     Like Field.setRegex() but pattern options re.S and re.M are
00258     automatically added.
00259     """
00260     patternstring,patternoptions = self._patternAndOptions(pattern)
00261     # This is a Unicode input field
00262     patternoptions = patternoptions | re.M|re.S
00263     Field.setRegex(self,(patternstring,patternoptions))
00264 
00265   def inputHTML(self,default=None,id_value=None,title=None):
00266     """Returns string with HTML input field."""
00267     return self.inputHTMLTemplate % (
00268       '<textarea %stitle="%s" name="%s" %s rows="%d" cols="%d">%s</textarea>' % (
00269         self.idAttrStr(id_value),
00270         self.titleHTML(title),
00271         self.name,
00272         self._accessKeyAttr(),
00273         self.rows,self.cols,
00274         self._defaultHTML(default)
00275       )
00276     )
00277 
00278   def valueHTML(self):
00279     """
00280     HTML output of self.value using the HTML template string
00281     in self.valueHTMLTemplate.
00282     """
00283     return [
00284       self.valueHTMLTemplate % '<pre>%s</pre>' % v
00285       for v in self.value
00286     ]
00287 
00288 
00289 class Input(Field):
00290   """
00291   Normal one-line input field:
00292   <input>
00293   """
00294 
00295   def __init__(
00296     self,name,text,maxLen,maxValues,pattern,required=0,default=None,accessKey='',
00297     size=None
00298   ):
00299     self.size = size or maxLen
00300     Field.__init__(
00301       self,name,text,maxLen,maxValues,pattern,required,default,accessKey,
00302     )
00303 
00304   def inputHTML(self,default=None,id_value=None,title=None):
00305     return self.inputHTMLTemplate % (
00306       '<input %stitle="%s" name="%s" %s maxlength="%d" size="%d" value="%s">' % (
00307         self.idAttrStr(id_value),
00308         self.titleHTML(title),
00309         self.name,
00310         self._accessKeyAttr(),
00311         self.maxLen,
00312         self.size,
00313         self._defaultHTML(default)
00314       )
00315     )
00316 
00317 
00318 class HiddenInput(Input):
00319   """
00320   Hidden input field:
00321   <input type="hidden">
00322   """
00323 
00324   def __init__(
00325     self,name,text,maxLen,maxValues,pattern,required=0,default=None,accessKey=''
00326   ):
00327     Input.__init__(
00328       self,name,text,maxLen,maxValues,pattern,required,default,accessKey,
00329     )
00330 
00331   def inputHTML(self,default=None,id_value=None,title=None,show=0):
00332     default_html = self._defaultHTML(default)
00333     if show:
00334       default_str = default_html
00335     else:
00336       default_str = ''
00337     return self.inputHTMLTemplate % (
00338       '<input type="hidden" %stitle="%s" name="%s" %s  value="%s">%s' % (
00339         self.idAttrStr(id_value),
00340         self.titleHTML(title),
00341         self.name,
00342         self._accessKeyAttr(),
00343         default_html,
00344         default_str
00345       )
00346     )
00347 
00348 
00349 class File(Input):
00350   """
00351   File upload field
00352   <input type="file">
00353   """
00354   mimeType='application/octet-stream'
00355 
00356   def _validateFormat(self,value):
00357     """Binary data is assumed to be valid all the time"""
00358     return
00359 
00360   def _encodeValue(self,value):
00361     """
00362     Return Unicode object or string to be stored in self.value
00363     """
00364     return value
00365 
00366   def inputHTML(self,default=None,id_value=None,title=None,mimeType=None):
00367     return self.inputHTMLTemplate % (
00368       '<input type="file" %stitle="%s" name="%s" %s accept="%s">' % (
00369         self.idAttrStr(id_value),
00370         self.titleHTML(title),
00371         self.name,
00372         self._accessKeyAttr(),
00373         mimeType or self.mimeType
00374       )
00375     )
00376 
00377 
00378 class Password(Input):
00379   """
00380   Password input field:
00381   <input type=password>
00382   """
00383 
00384   def inputHTML(self,default=None,id_value=None,title=None):
00385     return self.inputHTMLTemplate % (
00386       '<input %stitle="%s" name="%s" %s maxlength="%d" size="%d" type="password" value="%s">' % (
00387         self.idAttrStr(id_value),
00388         self.titleHTML(title),
00389         self.name,
00390         self._accessKeyAttr(),
00391         self.maxLen,
00392         self.size,
00393         default or ''
00394       )
00395     )
00396 
00397   def valueHTML(self):
00398     """For security reasons only stars are printed"""
00399     return [
00400       self.valueHTMLTemplate % (len(v)*'*')
00401       for v in self.value
00402     ]
00403 
00404 
00405 class Radio(Field):
00406   """
00407   Radio buttons:
00408   <INPUT TYPE=RADIO>
00409   """
00410 
00411   def __init__(
00412     self,name,text,maxValues=1,required=0,default=None,accessKey='',
00413     options=None
00414   ):
00415     """
00416     pattern and maxLen are determined from __init__ params
00417     Additional parameters:
00418     options=[]
00419       List of options. Either just a list of strings
00420       ['value1','value2',..] for simple options
00421       or a list of tuples of string pairs
00422       [('value1','description1),('value2','description2),..]
00423       for options with different option value and description.
00424     """
00425     self.setOptions(options)
00426     self.setDefault(default)
00427     Field.__init__(
00428       self,name,text,self.maxLen,maxValues,'',required,default,accessKey
00429     )
00430 
00431   def _validateFormat(self,value):
00432     """
00433     Check format of the user's value for this field.
00434 
00435     Empty input (zero-length string) are valid in any case.
00436     You might override this method to change this behaviour.
00437     """
00438     if value and (not value in self.optionValues):
00439       raise InvalidValueFormat(
00440         self.name,
00441         self.text.encode(self.charset),
00442         value.encode(self.charset)
00443       )
00444 
00445   def setOptions(self,options):
00446     self.optionValues = {}
00447     self.maxLen = 0
00448     if options:
00449       optionValues = []
00450       for i in options:
00451         if type(i) is types.TupleType:
00452           optionValue = i[0]
00453         else:
00454           optionValue = i
00455         self.optionValues[optionValue] = None
00456       self.maxLen = max(map(len,self.optionValues.keys()))
00457     self.options  = options
00458 
00459   def inputHTML(self,default=None,id_value=None,title=None):
00460     s = []
00461     default_value = self._defaultValue(default)
00462     for i in self.options:
00463       if type(i) is types.TupleType:
00464         optionValue,optionText = i
00465       else:
00466         optionValue = optionText = i
00467       s.append("""
00468         <input
00469           type="radio"
00470           %s
00471           title="%s"
00472           name="%s"
00473           %s
00474           value="%s"
00475           %s
00476         >%s<br>
00477         """ % (
00478           self.idAttrStr(id_value),
00479           self.titleHTML(title),
00480           self.name.encode(self.charset),
00481           self._accessKeyAttr(),
00482           optionValue,
00483           ' checked'*(optionValue==default_value),
00484           optionText
00485         )
00486       )
00487     return self.inputHTMLTemplate % '\n'.join(s)
00488 
00489   def setDefault(self,default):
00490     """
00491     Set the default of a default field.
00492 
00493     Mainly this is used if self.default shall be changed after
00494     initializing the field object.
00495     """
00496     optionValues = []
00497     for i in self.options:
00498       if type(i) is types.TupleType:
00499         optionValues.append(i[0])
00500       else:
00501         optionValues.append(i)
00502     if type(default)==types.StringType and not default in optionValues:
00503       # Append option to list of options if singleton
00504       self.options.append(default)
00505     elif type(default)==types.ListType:
00506       # Extend list of options with items in default which are not in options
00507       self.options.extend(filter(lambda x,o=optionValues:not x in o,default))
00508     self.default = default
00509 
00510 
00511 class Select(Radio):
00512   """
00513   Select field:
00514   <select multiple>
00515     <option value="value">description</option>
00516   </select>
00517   """
00518 
00519   def __init__(
00520     self,name,text,maxValues,required=0,default=None,accessKey='',
00521     options=None,size=1,ignoreCase=0,multiSelect=0,
00522   ):
00523     """
00524     Additional parameters:
00525     size
00526       Integer for the size of displayed select field.
00527     ignorecase
00528       Integer flag. If non-zero the case of input strings is ignored
00529       when checking input values.
00530     multiSelect
00531       Integer flag. If non-zero the select field has HTML attribute
00532       "multiple" set.
00533     """
00534     self.size        = size
00535     self.multiSelect = multiSelect
00536     self.ignoreCase  = ignoreCase
00537     Radio.__init__(
00538       self,name,text,maxValues,required,default,accessKey,options,
00539     )
00540 
00541   def inputHTML(self,default=None,id_value=None,title=None):
00542     s = ['<select %stitle="%s" name="%s" %s  size="%d" %s>' % (
00543       self.idAttrStr(id_value),
00544       self.titleHTML(title),
00545       self.name,
00546       self._accessKeyAttr(),
00547       self.size,
00548       " multiple"*(self.multiSelect>0)
00549     )]
00550     default_value = self._defaultValue(default)
00551     for i in self.options:
00552       if type(i) is types.TupleType:
00553         try:
00554           optionValue,optionText,optionTitle = i
00555         except ValueError:
00556           optionTitle = None
00557           optionValue,optionText = i
00558       else:
00559         optionTitle = None
00560         optionValue = optionText = i
00561       if self.multiSelect:
00562         option_selected = optionValue in default_value
00563       else:
00564         option_selected = (optionValue==default_value) or (self.ignoreCase and optionValue.lower()==default_value.lower())
00565       if optionTitle:
00566         optionTitle_attr = u' title="%s"' % escapeHTML(optionTitle.encode(self.charset))
00567       else:
00568         optionTitle_attr = ''
00569       s.append(
00570         '<option value="%s"%s%s>%s</option>' % (
00571           escapeHTML(optionValue.encode(self.charset)),
00572           optionTitle_attr,
00573           ' selected'*(option_selected),
00574           escapeHTML(optionText.encode(self.charset)),
00575         )
00576       )
00577     s.append('</select>')
00578     return self.inputHTMLTemplate % '\n'.join(s)
00579 
00580 
00581 class Checkbox(Field):
00582   """
00583   Check boxes:
00584   <INPUT TYPE=CHECKBOX>
00585   """
00586 
00587   def __init__(
00588     self,name,text,maxValues=1,required=0,accessKey='',
00589     default=None,checked=0
00590   ):
00591     """
00592     pattern and maxLen are determined by default
00593     """
00594     pattern = default
00595     maxLen = len(default or '')
00596     self.checked = checked
00597     Field.__init__(
00598       self,name,text,maxLen,maxValues,pattern,required,default,accessKey
00599     )
00600 
00601   def inputHTML(self,default=None,id_value=None,title=None,checked=None):
00602     if checked is None:
00603       checked = self.checked
00604     return self.inputHTMLTemplate % (
00605       '<input type="checkbox" %stitle="%s" name="%s" %s value="%s"%s>' % (
00606         self.idAttrStr(id_value),
00607         self.titleHTML(title),
00608         self.name,
00609         self._accessKeyAttr(),
00610         self._defaultValue(default),' checked'*(checked)
00611       )
00612     )
00613 
00614 
00615 class Keygen(Field):
00616   """
00617   Select field for client-side key generation with
00618   Netscape/Mozilla/Opera browser:
00619   <KEYGEN>
00620   """
00621 
00622   def __init__(
00623     self,name,text,maxLen,maxValues,required=0,
00624     minKeyLength=512
00625   ):
00626     Field.__init__(
00627       self,name,text,maxLen,maxValues,(r'[ -z\r\n]*',re.M+re.S),required
00628     )
00629     self.minKeyLength = minKeyLength
00630 
00631   def _encodeValue(self,value):
00632     return value.replace('\n','').replace('\r','')
00633 
00634   def inputHTML(self,challenge,id_value=None,title=None,):
00635     return self.inputHTMLTemplate % (
00636       '<keygen %stitle="%s" name="%s" %s challenge="%s">' % (
00637         self.idAttrStr(id_value),
00638         self.titleHTML(title),
00639         self.name,
00640         self._accessKeyAttr(),
00641         challenge
00642       )
00643     )
00644 
00645   def valueHTML(self):
00646     return self.valueHTMLTemplate % ('%d Bytes' % (len(self.value)))
00647 
00648 
00649 class FormException(Exception):
00650   """
00651   Base exception class to indicate form processing errors.
00652 
00653   Attributes:
00654   args          unstructured List of parameters
00655   """
00656   def __init__(self,*args,**kwargs):
00657     self.args = args
00658     for key,value in kwargs.items():
00659       setattr(self,key,value)
00660   def html(self):
00661     return escapeHTML(str(self))
00662 
00663 class InvalidRequestMethod(FormException):
00664   """
00665   Exception raised when HTTP request method was invalid.
00666 
00667   Attributes:
00668   method        string containing method used
00669   """
00670   def __init__(self,method):
00671     self.method = method
00672   def __str__(self):
00673     return 'Invalid request method %s.' % (self.method)
00674 
00675 class InvalidFormEncoding(FormException):
00676   """
00677   The form data is malformed.
00678 
00679   Attributes:
00680   param         name/value causing the exception
00681   """
00682   def __init__(self,param):
00683     self.param = param
00684   def __str__(self):
00685     return 'The form data is malformed.'
00686 
00687 class ContentLengthExceeded(FormException):
00688   """
00689   Overall length of input data too large.
00690 
00691   Attributes:
00692   contentLength         received content length
00693   maxContentLength      maximum valid content length
00694   """
00695   def __init__(self,contentLength,maxContentLength):
00696     self.contentLength = contentLength
00697     self.maxContentLength = maxContentLength
00698   def __str__(self):
00699     return 'Input length of %d bytes exceeded the maximum of %d bytes.' % (
00700       self.contentLength,self.maxContentLength
00701     )
00702 
00703 class UndeclaredFieldName(FormException):
00704   """
00705   Parameter with undeclared name attribute received.
00706 
00707   Attributes:
00708   name          name of undeclared field
00709   """
00710   def __init__(self,name):
00711     self.name = name
00712   def __str__(self):
00713     return 'Unknown parameter %s.' % (self.name)
00714 
00715 class ParamsMissing(FormException):
00716   """
00717   Required parameters are missing.
00718 
00719   Attributes:
00720   missingParamNames     list of strings containing all names of missing
00721                         input fields.
00722   """
00723   def __init__(self,missingParamNames):
00724     self.missingParamNames = missingParamNames
00725   def __str__(self):
00726     return 'Required fields missing: %s' % (
00727       ', '.join(
00728         map(
00729           lambda i:'%s (%s)' % (i[1],i[0]),
00730           self.missingParamNames
00731         )
00732       )
00733     )
00734 
00735 class InvalidValueFormat(FormException):
00736   """
00737   The user's input does not match the required format.
00738 
00739   Attributes:
00740   name          name of input field (Field.name)
00741   text          textual description of input field (Field.text)
00742   value         input value received
00743   """
00744   def __init__(self,name,text,value):
00745     self.name = name
00746     self.text = text
00747     self.value = value
00748   def __str__(self):
00749     return 'Input value "%s" for field %s (%s) has invalid format.' % (
00750       self.value,self.text,self.name
00751     )
00752 
00753 class InvalidValueLen(FormException):
00754   """
00755   Length of user input value invalid.
00756 
00757   Attributes:
00758   name          name of input field (Field.name)
00759   text          textual description of input field (Field.text)
00760   valueLen      integer number of received value length
00761   maxValueLen   integer number of maximum value length
00762   """
00763   def __init__(self,name,text,valueLen,maxValueLen):
00764     self.name      = name
00765     self.text      = text
00766     self.valueLen    = valueLen
00767     self.maxValueLen = maxValueLen
00768   def __str__(self):
00769     return 'Content too long. Field %s (%s) has %d characters but is limited to %d.' % (
00770       self.text,self.name,self.valueLen,self.maxValueLen
00771     )
00772 
00773 class TooManyValues(FormException):
00774   """
00775   User's input contained too many values for same parameter.
00776 
00777   Attributes:
00778   name                  name of input field (Field.name)
00779   text                  textual description of input field (Field.text)
00780   valueCount            integer number of values counted with name
00781   maxValueCount         integer number of maximum values with name allowed
00782   """
00783   def __init__(self,name,text,valueCount,maxValueCount):
00784     self.name      = name
00785     self.text      = text
00786     self.valueCount    = valueCount
00787     self.maxValueCount = maxValueCount
00788   def __str__(self):
00789     return '%d values for field %s (%s). Limited to %d input values.' % (
00790       self.valueCount,self.text,self.name,self.maxValueCount
00791     )
00792 
00793 
00794 class Form:
00795   """
00796   Class for declaring and processing a whole <form>
00797   """
00798 
00799   def __init__(self,inf,env):
00800     """
00801     Initialize a Form
00802     inf                 Read from this file object if method is POST.
00803     env                 Dictionary holding the environment vars.
00804     """
00805     # Dictionary of Field objects
00806     self.field = {}
00807     # Ordered list of input field names
00808     self.declaredFieldNames = []
00809     # List of parameters names received
00810     self.inputFieldNames = []
00811     # Save the environment vars
00812     self.env = env
00813     # input file object
00814     self.inf = inf or sys.stdin
00815     # Save request method
00816     self.request_method = env['REQUEST_METHOD']
00817     self.script_name = env['SCRIPT_NAME']
00818     # Initialize the AcceptHeaderDict objects
00819     self.http_accept_charset = helper.AcceptCharsetDict('HTTP_ACCEPT_CHARSET',env)
00820     self.http_accept_language = helper.AcceptHeaderDict('HTTP_ACCEPT_LANGUAGE',env)
00821     self.accept_language = self.http_accept_language.keys()
00822     self.http_accept_encoding = helper.AcceptHeaderDict('HTTP_ACCEPT_ENCODING',env)
00823     # Set the preferred character set
00824     self.accept_charset = self.http_accept_charset.preferred()
00825     # Determine query string and content length dependent on request method
00826     self.checkRequestMethod()
00827     return # Form.__init__()
00828 
00829   def checkRequestMethod(self):
00830     """
00831     Checks whether the HTTP request method is accepted
00832     """
00833     if not self.request_method in ['POST','GET']:
00834       raise InvalidRequestMethod(self.request_method)
00835 
00836   def getContentType(self):
00837     """
00838     Determine the HTTP content type of HTTP request
00839     """
00840     if self.request_method=='POST':
00841       return self.env.get('CONTENT_TYPE','application/x-www-form-urlencoded').lower() or None
00842     elif self.request_method=='GET':
00843       return 'application/x-www-form-urlencoded'
00844 
00845   def addField(self,f):
00846     """
00847     Add a input field object f to the form.
00848     """
00849     f.setCharset(self.accept_charset)
00850     self.field[f.name] = f
00851     if not f.name in self.declaredFieldNames:
00852       self.declaredFieldNames.append(f.name)
00853     return # Form.addField()
00854 
00855   def getInputValue(self,name,default=[]):
00856     """
00857     Return input value of a field defined by name if presented
00858     in form input. Return default else.
00859     """
00860     if name in self.inputFieldNames:
00861       return self.field[name].value
00862     else:
00863       return default
00864 
00865   def hiddenInputFields(self,outf=sys.stdout,ignoreFieldNames=None):
00866     """
00867     Output all parameters as hidden fields.
00868 
00869     outf
00870         File object for output.
00871     ignoreFieldNames
00872         Names of parameters to be excluded.
00873     """
00874     ignoreFieldNames=ignoreFieldNames or []
00875     for f in [
00876       self.field[p]
00877       for p in self.declaredFieldNames
00878       if (p in self.inputFieldNames) and not (p in ignoreFieldNames)
00879     ]:
00880       for v in f.value:
00881         outf.write(
00882           '<input type="hidden" name="%s" value="%s">\n\r' % (
00883             f.name.encode(f.charset),escapeHTML(v.encode(f.charset))
00884           )
00885         )
00886     return # Form.hiddenInputFields()
00887 
00888   def _parseFormUrlEncoded(self,maxContentLength,ignoreEmptyFields,ignoreUndeclaredFields,stripValues,unquote):
00889 
00890     if self.request_method=='POST':
00891       query_string = self.inf.read(int(self.env['CONTENT_LENGTH']))
00892     elif self.request_method=='GET':
00893       query_string = self.env.get('QUERY_STRING','')
00894 
00895     self.inf.close()
00896 
00897     inputlist = query_string.split('&')
00898 
00899     contentLength = 0
00900 
00901     # Loop over all name attributes declared
00902     for param in inputlist:
00903 
00904       if param:
00905 
00906         # Einzelne Parametername/-daten-Paare auseinandernehmen
00907         try:
00908           name,value = param.split('=',1)
00909         except ValueError:
00910           raise InvalidFormEncoding(param)
00911         name = unquote(name).strip()
00912 
00913         if not name in self.declaredFieldNames:
00914           if ignoreUndeclaredFields:
00915             continue
00916           else:
00917             raise UndeclaredFieldName(name)
00918 
00919         value = unquote(value)
00920         if stripValues:
00921           value = value.strip()
00922 
00923         contentLength += len(value)
00924         # Gesamtlaenge der Daten noch zulaessig?
00925         if contentLength > maxContentLength:
00926           raise ContentLengthExceeded(contentLength,maxContentLength)
00927 
00928         f = self.field[name]
00929 
00930         # input is empty string?
00931         if value or (not ignoreEmptyFields):
00932           # Input is stored in field instance
00933           f.setValue(value)
00934           # Add name of field to list of input keys
00935           if not name in self.inputFieldNames:
00936             self.inputFieldNames.append(name)
00937 
00938     return #_parseFormUrlEncoded()
00939 
00940 
00941   def _parseMultipartFormData(self,maxContentLength,ignoreEmptyFields,ignoreUndeclaredFields,stripValues,unquote):
00942 
00943     import cgi
00944     ctype, pdict = cgi.parse_header(self.env['CONTENT_TYPE'])
00945     parts = cgi.parse_multipart(self.inf,pdict)
00946 
00947     contentLength = 0
00948 
00949     for name in parts.keys():
00950 
00951       if not name in self.declaredFieldNames:
00952         if ignoreUndeclaredFields:
00953           continue
00954         else:
00955           raise UndeclaredFieldName(name)
00956 
00957       for value in parts[name]:
00958 
00959 #        if stripValues:
00960 #     value = value.strip()
00961 
00962         contentLength += len(value)
00963         # Gesamtlaenge der Daten noch zulaessig?
00964         if contentLength > maxContentLength:
00965           raise ContentLengthExceeded(contentLength,maxContentLength)
00966 
00967         f = self.field[name]
00968 
00969         # input is empty string?
00970         if value or (not ignoreEmptyFields):
00971           # Input is stored in field instance
00972           f.setValue(value)
00973           # Add name of field to list of input keys
00974           if not name in self.inputFieldNames:
00975             self.inputFieldNames.append(name)
00976 
00977     return # _parseMultipartFormData()
00978 
00979 
00980   def getInputFields(
00981     self,
00982     ignoreEmptyFields=0,
00983     ignoreUndeclaredFields=0,
00984     stripValues=1,
00985     unquotePlus=0,
00986   ):
00987     """
00988     Process user's <form> input and store the values in each
00989     field instance's content attribute.
00990 
00991     When a processing error occurs FormException (or derivatives)
00992     are raised.
00993 
00994     ignoreEmptyFields=0         Ignore fields with empty input.
00995     ignoreUndeclaredFields=0    Ignore fields with names not declared.
00996                                 Normally UndeclaredFieldName is raised.
00997     stripValues=1               If true leading and trailing whitespaces
00998                                 are stripped from all input values.
00999     unquotePlus=0
01000        If non-zero urllib.unquote_plus() is used instead of urllib.unquote().
01001     """
01002 
01003     unquote = {0:urllib.unquote_plus,1:urllib.unquote_plus}[unquotePlus]
01004 
01005     # Calculate maxContentLength
01006     maxContentLength = 0
01007     for name,f in self.field.items():
01008       maxContentLength += f.maxValues*f.maxLen
01009 
01010     content_type = self.getContentType()
01011     if content_type.startswith('application/x-www-form-urlencoded'):
01012       # Parse user's input
01013       self._parseFormUrlEncoded(maxContentLength,ignoreEmptyFields,ignoreUndeclaredFields,stripValues,unquote)
01014     elif content_type.startswith('multipart/form-data'):
01015       self._parseMultipartFormData(maxContentLength,ignoreEmptyFields,ignoreUndeclaredFields,stripValues,unquote)
01016     else:
01017       raise FormException('Invalid content type %s received' % (repr(content_type)))
01018 
01019 
01020     # Are all required parameters present?
01021     missing_params = []
01022     for n,f in self.field.items():
01023       if f.required and not (f.name in self.inputFieldNames):
01024         missing_params.append((f.name,f.text))
01025     if missing_params:
01026       raise ParamsMissing(missing_params)
01027 
01028     return # Form.getInputFields()