HTTPRequest.py
上传用户:gyjinxi
上传日期:2007-01-04
资源大小:159k
文件大小:36k
源码类别:

WEB邮件程序

开发平台:

Python

  1. ##############################################################################
  2. # Zope Public License (ZPL) Version 1.0
  3. # -------------------------------------
  4. # Copyright (c) Digital Creations.  All rights reserved.
  5. # This license has been certified as Open Source(tm).
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions are
  8. # met:
  9. # 1. Redistributions in source code must retain the above copyright
  10. #    notice, this list of conditions, and the following disclaimer.
  11. # 2. Redistributions in binary form must reproduce the above copyright
  12. #    notice, this list of conditions, and the following disclaimer in
  13. #    the documentation and/or other materials provided with the
  14. #    distribution.
  15. # 3. Digital Creations requests that attribution be given to Zope
  16. #    in any manner possible. Zope includes a "Powered by Zope"
  17. #    button that is installed by default. While it is not a license
  18. #    violation to remove this button, it is requested that the
  19. #    attribution remain. A significant investment has been put
  20. #    into Zope, and this effort will continue if the Zope community
  21. #    continues to grow. This is one way to assure that growth.
  22. # 4. All advertising materials and documentation mentioning
  23. #    features derived from or use of this software must display
  24. #    the following acknowledgement:
  25. #      "This product includes software developed by Digital Creations
  26. #      for use in the Z Object Publishing Environment
  27. #      (http://www.zope.org/)."
  28. #    In the event that the product being advertised includes an
  29. #    intact Zope distribution (with copyright and license included)
  30. #    then this clause is waived.
  31. # 5. Names associated with Zope or Digital Creations must not be used to
  32. #    endorse or promote products derived from this software without
  33. #    prior written permission from Digital Creations.
  34. # 6. Modified redistributions of any form whatsoever must retain
  35. #    the following acknowledgment:
  36. #      "This product includes software developed by Digital Creations
  37. #      for use in the Z Object Publishing Environment
  38. #      (http://www.zope.org/)."
  39. #    Intact (re-)distributions of any official Zope release do not
  40. #    require an external acknowledgement.
  41. # 7. Modifications are encouraged but must be packaged separately as
  42. #    patches to official Zope releases.  Distributions that do not
  43. #    clearly separate the patches from the original work must be clearly
  44. #    labeled as unofficial distributions.  Modifications which do not
  45. #    carry the name Zope may be packaged in any form, as long as they
  46. #    conform to all of the clauses above.
  47. # Disclaimer
  48. #   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
  49. #   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  50. #   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  51. #   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
  52. #   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  53. #   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  54. #   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  55. #   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  56. #   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  57. #   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  58. #   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  59. #   SUCH DAMAGE.
  60. # This software consists of contributions made by Digital Creations and
  61. # many individuals on behalf of Digital Creations.  Specific
  62. # attributions are listed in the accompanying credits file.
  63. ##############################################################################
  64. __version__='$Revision: 1.26.4.1 $'[11:-2]
  65. import regex, sys, os, string
  66. from string import lower, atoi, rfind, split, strip, join, upper, find
  67. from BaseRequest import BaseRequest
  68. from HTTPResponse import HTTPResponse
  69. from cgi import FieldStorage
  70. from urllib import quote, unquote
  71. from Converters import get_converter
  72. from maybe_lock import allocate_lock
  73. xmlrpc=None # Placeholder for module that we'll import if we have to.
  74. isCGI_NAME = {
  75.         'SERVER_SOFTWARE' : 1, 
  76.         'SERVER_NAME' : 1, 
  77.         'GATEWAY_INTERFACE' : 1, 
  78.         'SERVER_PROTOCOL' : 1, 
  79.         'SERVER_PORT' : 1, 
  80.         'REQUEST_METHOD' : 1, 
  81.         'PATH_INFO' : 1, 
  82.         'PATH_TRANSLATED' : 1, 
  83.         'SCRIPT_NAME' : 1, 
  84.         'QUERY_STRING' : 1, 
  85.         'REMOTE_HOST' : 1, 
  86.         'REMOTE_ADDR' : 1, 
  87.         'AUTH_TYPE' : 1, 
  88.         'REMOTE_USER' : 1, 
  89.         'REMOTE_IDENT' : 1, 
  90.         'CONTENT_TYPE' : 1, 
  91.         'CONTENT_LENGTH' : 1,
  92.         'SERVER_URL': 1,
  93.         }.has_key
  94. hide_key={'HTTP_AUTHORIZATION':1,
  95.           'HTTP_CGI_AUTHORIZATION': 1,
  96.           }.has_key
  97. _marker=[]
  98. class HTTPRequest(BaseRequest):
  99.     """
  100.     Model HTTP request data.
  101.     
  102.     This object provides access to request data.  This includes, the
  103.     input headers, form data, server data, and cookies.
  104.     Request objects are created by the object publisher and will be
  105.     passed to published objects through the argument name, REQUEST.
  106.     The request object is a mapping object that represents a
  107.     collection of variable to value mappings.  In addition, variables
  108.     are divided into four categories:
  109.       - Environment variables
  110.         These variables include input headers, server data, and other
  111.         request-related data.  The variable names are as <a
  112.         href="http://hoohoo.ncsa.uiuc.edu/cgi/env.html">specified</a>
  113.         in the <a
  114.         href="http://hoohoo.ncsa.uiuc.edu/cgi/interface.html">CGI
  115.         specification</a>
  116.       - Form data
  117.         These are data extracted from either a URL-encoded query
  118.         string or body, if present.
  119.       - Cookies
  120.         These are the cookie data, if present.
  121.       - Other
  122.         Data that may be set by an application object.
  123.     The form attribute of a request is actually a Field Storage
  124.     object.  When file uploads are used, this provides a richer and
  125.     more complex interface than is provided by accessing form data as
  126.     items of the request.  See the FieldStorage class documentation
  127.     for more details.
  128.     The request object may be used as a mapping object, in which case
  129.     values will be looked up in the order: environment variables,
  130.     other variables, form data, and then cookies.
  131.     """
  132.     _hacked_path=None
  133.     args=()
  134.     _file=None
  135.     retry_max_count=3
  136.     def supports_retry(self): return self.retry_count < self.retry_max_count
  137.     def retry(self):
  138.         self.retry_count=self.retry_count+1
  139.         self.stdin.seek(0)
  140.         r=self.__class__(stdin=self.stdin,
  141.                          environ=self._orig_env,
  142.                          response=self.response.retry()
  143.                          )
  144.         return r
  145.     def __init__(self, stdin, environ, response, clean=0):
  146.         self._orig_env=environ
  147.         # Avoid the overhead of scrubbing the environment in the
  148.         # case of request cloning for traversal purposes. If the
  149.         # clean flag is set, we know we can use the passed in
  150.         # environ dict directly.
  151.         if not clean: environ=sane_environment(environ)
  152.         if environ.has_key('HTTP_AUTHORIZATION'):
  153.             self._auth=environ['HTTP_AUTHORIZATION']
  154.             response._auth=1
  155.             del environ['HTTP_AUTHORIZATION']
  156.         
  157.         self.stdin=stdin
  158.         self.environ=environ
  159.         have_env=environ.has_key
  160.         get_env=environ.get
  161.         self.response=response
  162.         other=self.other={'RESPONSE': response}
  163.         self.form={}
  164.         self.steps=[]
  165.         ################################################################
  166.         # Get base info first. This isn't likely to cause
  167.         # errors and might be useful to error handlers.
  168.         b=script=strip(get_env('SCRIPT_NAME',''))
  169.         while b and b[-1]=='/': b=b[:-1]
  170.         p = rfind(b,'/')
  171.         if p >= 0: b=b[:p+1]
  172.         else: b=''
  173.         while b and b[0]=='/': b=b[1:]
  174.         server_url=get_env('SERVER_URL',None)
  175.         if server_url is not None:
  176.              server_url=strip(server_url)
  177.         else:
  178.              if have_env('HTTPS') and (
  179.                  environ['HTTPS'] == "on" or environ['HTTPS'] == "ON"):
  180.                  server_url='https://'
  181.              elif (have_env('SERVER_PORT_SECURE') and 
  182.                    environ['SERVER_PORT_SECURE'] == "1"):
  183.                  server_url='https://'
  184.              else: server_url='http://'
  185.              if have_env('HTTP_HOST'):
  186.                  server_url=server_url+strip(environ['HTTP_HOST'])
  187.              else:
  188.                  server_url=server_url+strip(environ['SERVER_NAME'])
  189.                  server_port=environ['SERVER_PORT']
  190.                  if server_port!='80': server_url=server_url+':'+server_port
  191.              other['SERVER_URL']=server_url
  192.              
  193.         if server_url[-1:]=='/': server_url=server_url[:-1]
  194.                         
  195.         if b: self.base="%s/%s" % (server_url,b)
  196.         else: self.base=server_url
  197.         while script[:1]=='/': script=script[1:]
  198.         if script: script="%s/%s" % (server_url,script)
  199.         else:      script=server_url
  200.         other['URL']=self.script=script
  201.         ################################################################
  202.         # Cookie values should *not* be appended to existing form
  203.         # vars with the same name - they are more like default values
  204.         # for names not otherwise specified in the form.
  205.         cookies={}
  206.         k=get_env('HTTP_COOKIE','')
  207.         if k:
  208.             parse_cookie(k, cookies)
  209.             for k,item in cookies.items():
  210.                 if not other.has_key(k):
  211.                     other[k]=item
  212.         self.cookies=cookies
  213.     
  214.     def processInputs(
  215.         self,
  216.         # "static" variables that we want to be local for speed
  217.         SEQUENCE=1,
  218.         DEFAULT=2,
  219.         RECORD=4,
  220.         RECORDS=8,
  221.         REC=12, # RECORD|RECORDS
  222.         EMPTY=16,
  223.         CONVERTED=32,
  224.         hasattr=hasattr,
  225.         getattr=getattr,
  226.         setattr=setattr,
  227.         search_type=regex.compile(':[a-zA-Z][a-zA-Z0-9_]+$').search,
  228.         rfind=string.rfind,
  229.         ):
  230.         """Process request inputs
  231.         We need to delay input parsing so that it is done under
  232.         publisher control for error handling purposes.
  233.         """
  234.         response=self.response
  235.         environ=self.environ
  236.         method=environ.get('REQUEST_METHOD','GET')
  237.         
  238.         if method != 'GET': fp=self.stdin
  239.         else:               fp=None
  240.         form=self.form
  241.         other=self.other
  242.         meth=None
  243.         fs=FieldStorage(fp=fp,environ=environ,keep_blank_values=1)
  244.         if not hasattr(fs,'list') or fs.list is None:
  245.             # Hm, maybe it's an XML-RPC
  246.             if (fs.headers.has_key('content-type') and
  247.                 fs.headers['content-type'] == 'text/xml' and
  248.                 method == 'POST'):
  249.                 # Ye haaa, XML-RPC!
  250.                 global xmlrpc
  251.                 if xmlrpc is None: import xmlrpc
  252.                 meth, self.args = xmlrpc.parse_input(fs.value)
  253.                 response=xmlrpc.response(response)
  254.                 other['RESPONSE']=self.response=response
  255.                 other['REQUEST_METHOD']='' # We don't want index_html!
  256.             else:
  257.                 self._file=fs.file
  258.         else:
  259.             fslist=fs.list
  260.             tuple_items={}
  261.             lt=type([])
  262.             CGI_name=isCGI_NAME
  263.             defaults={}
  264.             converter=seqf=None
  265.             
  266.             for item in fslist:
  267.                 
  268.                 key=item.name
  269.                 if (hasattr(item,'file') and hasattr(item,'filename')
  270.                     and hasattr(item,'headers')):
  271.                     if (item.file and
  272.                         (item.filename is not None
  273.                          # RFC 1867 says that all fields get a content-type.
  274.                          # or 'content-type' in map(lower, item.headers.keys())
  275.                          )):
  276.                         item=FileUpload(item)
  277.                     else:
  278.                         item=item.value
  279.                 flags=0
  280.                 # Loop through the different types and set
  281.                 # the appropriate flags
  282.                 # We'll search from the back to the front.
  283.                 # We'll do the search in two steps.  First, we'll
  284.                 # do a string search, and then we'll check it with
  285.                 # a regex search.
  286.                 
  287.                 l=rfind(key,':')
  288.                 if l >= 0:
  289.                     l=search_type(key,l)
  290.                     while l >= 0:
  291.                         type_name=key[l+1:]
  292.                         key=key[:l]
  293.                         c=get_converter(type_name, None)
  294.                         if c is not None: 
  295.                             converter=c
  296.                             flags=flags|CONVERTED
  297.                         elif type_name == 'list':
  298.                             seqf=list
  299.                             flags=flags|SEQUENCE
  300.                         elif type_name == 'tuple':
  301.                             seqf=tuple
  302.                             tuple_items[key]=1
  303.                             flags=flags|SEQUENCE
  304.                         elif type_name == 'method':
  305.                             if l: meth=key
  306.                             else: meth=item
  307.                         elif type_name == 'default_method':
  308.                             if not meth:
  309.                                 if l: meth=key
  310.                                 else: meth=item
  311.                         elif type_name == 'default':
  312.                             flags=flags|DEFAULT
  313.                         elif type_name == 'record':
  314.                             flags=flags|RECORD
  315.                         elif type_name == 'records':
  316.                             flags=flags|RECORDS
  317.                         elif type_name == 'ignore_empty':
  318.                             if not item: flags=flags|EMPTY
  319.     
  320.                         l=rfind(key,':')
  321.                         if l < 0: break
  322.                         l=search_type(key,l)
  323.              
  324.                 # Filter out special names from form:
  325.                 if CGI_name(key) or key[:5]=='HTTP_': continue
  326.                 if flags:
  327.                     # skip over empty fields    
  328.                     if flags&EMPTY: continue
  329.                     #Split the key and its attribute
  330.                     if flags&REC:
  331.                         key=split(key,".")
  332.                         key, attr=join(key[:-1],"."), key[-1]
  333.                        
  334.                     # defer conversion
  335.                     if flags&CONVERTED:
  336.                         try:
  337.                             item=converter(item)
  338.                         except:
  339.                             if (not item and not (flags&DEFAULT) and
  340.                                 defaults.has_key(key)):
  341.                                 item = defaults[key]
  342.                                 if flags&RECORD:
  343.                                     item=getattr(item,attr)
  344.                                 if flags&RECORDS:
  345.                                     item.reverse()
  346.                                     item = item[0]
  347.                                     item=getattr(item,attr)
  348.                             else:
  349.                                 raise                            
  350.                          
  351.                     #Determine which dictionary to use
  352.                     if flags&DEFAULT:
  353.                        mapping_object = defaults
  354.                     else:
  355.                        mapping_object = form
  356.                     #Insert in dictionary
  357.                     if mapping_object.has_key(key):
  358.                        if flags&RECORDS:
  359.                            #Get the list and the last record
  360.                            #in the list
  361.                            reclist = mapping_object[key]
  362.                            reclist.reverse()
  363.                            x=reclist[0]
  364.                            reclist.reverse()
  365.                            if not hasattr(x,attr):
  366.                                #If the attribute does not
  367.                                #exist, set it
  368.                                if flags&SEQUENCE: item=[item]
  369.                                reclist.remove(x)
  370.                                setattr(x,attr,item)
  371.                                reclist.append(x)
  372.                                mapping_object[key] = reclist
  373.                            else:
  374.                                if flags&SEQUENCE:
  375.                                    # If the attribute is a
  376.                                    # sequence, append the item
  377.                                    # to the existing attribute
  378.                                    reclist.remove(x)
  379.                                    y = getattr(x, attr)
  380.                                    y.append(item)
  381.                                    setattr(x, attr, y)
  382.                                    reclist.append(x)
  383.                                    mapping_object[key] = reclist
  384.                                else:
  385.                                    # Create a new record and add
  386.                                    # it to the list
  387.                                    n=record()
  388.                                    setattr(n,attr,item)
  389.                                    reclist.append(n)
  390.                                    mapping_object[key]=reclist
  391.                        elif flags&RECORD:
  392.                            b=mapping_object[key]
  393.                            if flags&SEQUENCE:
  394.                               item=[item]
  395.                               if not hasattr(b,attr):
  396.                                   # if it does not have the
  397.                                   # attribute, set it
  398.                                   setattr(b,attr,item)
  399.                               else:
  400.                                   # it has the attribute so
  401.                                   # append the item to it
  402.                                   setattr(b,attr,getattr(b,attr)+item)
  403.                            else:
  404.                               # it is not a sequence so
  405.                               # set the attribute
  406.                               setattr(b,attr,item)        
  407.                        else:
  408.                           # it is not a record or list of records
  409.                            found=mapping_object[key]
  410.                            if type(found) is lt:
  411.                                found.append(item)
  412.                            else:
  413.                                found=[found,item]
  414.                                mapping_object[key]=found
  415.                     else:
  416.                        # The dictionary does not have the key
  417.                        if flags&RECORDS:
  418.                            # Create a new record, set its attribute
  419.                            # and put it in the dictionary as a list
  420.                            a = record()
  421.                            if flags&SEQUENCE: item=[item]
  422.                            setattr(a,attr,item)
  423.                            mapping_object[key]=[a]
  424.                        elif flags&RECORD:
  425.                            # Create a new record, set its attribute
  426.                            # and put it in the dictionary
  427.                            if flags&SEQUENCE: item=[item]
  428.                            r = mapping_object[key]=record()
  429.                            setattr(r,attr,item)
  430.                        else:
  431.                            # it is not a record or list of records
  432.                            if flags&SEQUENCE: item=[item]
  433.                            mapping_object[key]=item
  434.                 else:
  435.                     # This branch is for case when no type was specified.
  436.                     mapping_object = form
  437.     
  438.                     #Insert in dictionary
  439.                     if mapping_object.has_key(key):
  440.                         # it is not a record or list of records
  441.                         found=mapping_object[key]
  442.                         if type(found) is lt:
  443.                             found.append(item)
  444.                         else:
  445.                             found=[found,item]
  446.                             mapping_object[key]=found
  447.                     else:
  448.                         mapping_object[key]=item
  449.             #insert defaults into form dictionary
  450.             if defaults:
  451.                 for keys, values in defaults.items():
  452.                     if not form.has_key(keys):
  453.                         # if the form does not have the key,
  454.                         # set the default
  455.                         form[keys]=values
  456.                     else:
  457.                         #The form has the key
  458.                         if getattr(values, '__class__',0) is record:
  459.                            # if the key is mapped to a record, get the
  460.                            # record
  461.                            r = form[keys]
  462.                            for k, v in values.__dict__.items():
  463.                               # loop through the attributes and values
  464.                               # in the default dictionary
  465.                               if not hasattr(r, k):
  466.                                  # if the form dictionary doesn't have
  467.                                  # the attribute, set it to the default
  468.                                  setattr(r,k,v)
  469.                                  form[keys] = r    
  470.                         elif values == type([]):
  471.                                # the key is mapped to a list
  472.                                l = form[keys]
  473.                                for x in values:
  474.                                    # for each x in the list
  475.                                    if getattr(x, '__class__',0) is record:
  476.                                        # if the x is a record
  477.                                        for k, v in x.__dict__.items():
  478.                                            
  479.                                            # loop through each
  480.                                            # attribute and value in
  481.                                            # the record
  482.                                            
  483.                                            for y in l:
  484.                                                
  485.                                                # loop through each
  486.                                                # record in the form
  487.                                                # list if it doesn't
  488.                                                # have the attributes
  489.                                                # in the default
  490.                                                # dictionary, set them
  491.                                                
  492.                                                if not hasattr(y, k):
  493.                                                    setattr(y, k, v)
  494.                                    else:
  495.                                        # x is not a record
  496.                                        if not a in l:
  497.                                            l.append(a)
  498.                                form[keys] = l
  499.                         else:
  500.                             # The form has the key, the key is not mapped
  501.                             # to a record or sequence so do nothing
  502.                             pass
  503.                                 
  504.             # Convert to tuples
  505.             if tuple_items:
  506.                 for key in tuple_items.keys():
  507.                    # Split the key and get the attr
  508.                    k=split(key, ".")
  509.                    k,attr=join(k[:-1], "."), k[-1]
  510.                    a = attr
  511.                    # remove any type_names in the attr
  512.                    while not a=='':
  513.                       a=split(a, ":")
  514.                       a,new=join(a[:-1], ":"), a[-1]
  515.                    attr = new
  516.                    if form.has_key(k):
  517.                       # If the form has the split key get its value
  518.                       item =form[k]
  519.                       if (hasattr(item, '__class__') and
  520.                           item.__class__ is record):
  521.                          # if the value is mapped to a record, check if it
  522.                          # has the attribute, if it has it, convert it to
  523.                          # a tuple and set it
  524.                          if hasattr(item,attr):
  525.                             value=tuple(getattr(item,attr))
  526.                             setattr(item,attr,value)
  527.                       else:
  528.                          # It is mapped to a list of  records
  529.                          for x in item:
  530.                             # loop through the records
  531.                             if hasattr(x, attr):
  532.                                # If the record has the attribute
  533.                                # convert it to a tuple and set it
  534.                                value=tuple(getattr(x,attr))
  535.                                setattr(x,attr,value)          
  536.                    else:
  537.                       # the form does not have the split key 
  538.                       if form.has_key(key):
  539.                          # if it has the original key, get the item
  540.                          # convert it to a tuple
  541.                          item=form[key]  
  542.                          item=tuple(form[key])
  543.                          form[key]=item
  544.                      
  545.         other.update(form)
  546.         if meth:
  547.             if environ.has_key('PATH_INFO'):
  548.                 path=environ['PATH_INFO']
  549.                 while path[-1:]=='/': path=path[:-1]
  550.             else: path=''
  551.             other['PATH_INFO']=path="%s/%s" % (path,meth)
  552.             self._hacked_path=1
  553.     def resolve_url(self, url):
  554.         # Attempt to resolve a url into an object in the Zope
  555.         # namespace. The url must be a fully-qualified url. The
  556.         # method will return the requested object if it is found
  557.         # or raise the same HTTP error that would be raised in
  558.         # the case of a real web request. If the passed in url
  559.         # does not appear to describe an object in the system
  560.         # namespace (e.g. the host, port or script name dont
  561.         # match that of the current request), a ValueError will
  562.         # be raised.
  563.         if find(url, self.script) != 0:
  564.             raise ValueError, 'Different namespace.'
  565.         path=url[len(self.script):]
  566.         while path and path[0]=='/':  path=path[1:]
  567.         while path and path[-1]=='/': path=path[:-1]
  568.         req=self.clone()
  569.         rsp=req.response
  570.         req['PATH_INFO']=path
  571.         object=None
  572.         
  573.         # Try to traverse to get an object. Note that we call
  574.         # the exception method on the response, but we don't
  575.         # want to actually abort the current transaction
  576.         # (which is usually the default when the exception
  577.         # method is called on the response).
  578.         try: object=req.traverse(path)
  579.         except: rsp.exception(abort=0)
  580.         if object is None:
  581.             req.close()
  582.             raise rsp.errmsg, sys.exc_value
  583.         # The traversal machinery may return a "default object"
  584.         # like an index_html document. This is not appropriate
  585.         # in the context of the resolve_url method so we need
  586.         # to ensure we are getting the actual object named by
  587.         # the given url, and not some kind of default object.
  588.         if hasattr(object, 'id'):
  589.             if callable(object.id):
  590.                 name=object.id()
  591.             else: name=object.id
  592.         elif hasattr(object, '__name__'):
  593.             name=object.__name__
  594.         else: name=''
  595.         if name != os.path.split(path)[-1]:
  596.             object=req.PARENTS[0]
  597.         req.close()
  598.         return object
  599.         
  600.     def clone(self):
  601.         # Return a clone of the current request object 
  602.         # that may be used to perform object traversal.
  603.         environ=self.environ.copy()
  604.         environ['REQUEST_METHOD']='GET'
  605.         if self._auth: environ['HTTP_AUTHORIZATION']=self._auth
  606.         clone=HTTPRequest(None, environ, HTTPResponse(), clean=1)
  607.         clone['PARENTS']=[self['PARENTS'][-1]]
  608.         return clone
  609.     def get_header(self, name, default=None):
  610.         """Return the named HTTP header, or an optional default
  611.         argument or None if the header is not found. Note that
  612.         both original and CGI-ified header names are recognized,
  613.         e.g. 'Content-Type', 'CONTENT_TYPE' and 'HTTP_CONTENT_TYPE'
  614.         should all return the Content-Type header, if available.
  615.         """
  616.         environ=self.environ
  617.         name=upper(join(split(name,"-"),"_"))
  618.         val=environ.get(name, None)
  619.         if val is not None:
  620.             return val
  621.         if name[:5] != 'HTTP_':
  622.             name='HTTP_%s' % name
  623.         return environ.get(name, default)
  624.     def __getitem__(self,key,
  625.                     default=_marker, # Any special internal marker will do
  626.                     URLmatch=regex.compile('URL[0-9]+$').match,
  627.                     BASEmatch=regex.compile('BASE[0-9]+$').match,
  628.                     ):
  629.         """Get a variable value
  630.         Return a value for the required variable name.
  631.         The value will be looked up from one of the request data
  632.         categories. The search order is environment variables,
  633.         other variables, form data, and then cookies. 
  634.         
  635.         """ #"
  636.         other=self.other
  637.         if other.has_key(key):
  638.             if key=='REQUEST': return self
  639.             return other[key]
  640.         if key[:1]=='U' and URLmatch(key) >= 0:
  641.             n=atoi(key[3:])
  642.             URL=other['URL']
  643.             for i in range(0,n):
  644.                 l=rfind(URL,'/')
  645.                 if l >= 0: URL=URL[:l]
  646.                 else: raise KeyError, key
  647.                 if len(URL) < len(self.base) and n > 1: raise KeyError, key
  648.             other[key]=URL
  649.             return URL
  650.         if isCGI_NAME(key) or key[:5] == 'HTTP_':
  651.             environ=self.environ
  652.             if environ.has_key(key) and (not hide_key(key)):
  653.                 return environ[key]
  654.             return ''
  655.         if key=='REQUEST': return self
  656.         if key[:1]=='B':
  657.             if BASEmatch(key) >= 0:
  658.                 n=atoi(key[4:])
  659.                 if n:
  660.                     n=n-1
  661.                     
  662.                     if len(self.steps) < n:
  663.                         raise KeyError, key
  664.                     v=self.script
  665.                     while v[-1:]=='/': v=v[:-1]
  666.                     v=join([v]+self.steps[:n],'/')
  667.                 else:
  668.                     v=self.base
  669.                     while v[-1:]=='/': v=v[:-1]
  670.                 other[key]=v
  671.                 return v
  672.             if key=='BODY' and self._file is not None:
  673.                 p=self._file.tell()
  674.                 self._file.seek(0)
  675.                 v=self._file.read()
  676.                 self._file.seek(p)
  677.                 self.other[key]=v
  678.                 return v
  679.             if key=='BODYFILE' and self._file is not None:
  680.                 v=self._file
  681.                 self.other[key]=v
  682.                 return v
  683.         v=self.common.get(key, default)
  684.         if v is not _marker: return v
  685.         raise KeyError, key
  686.     __getattr__=__getitem__
  687.     def get(self, key, default=None):
  688.         return self.__getitem__(key, default)
  689.     def has_key(self, key):
  690.         try: self[key]
  691.         except: return 0
  692.         else: return 1
  693.     def keys(self):
  694.         keys = {}
  695.         keys.update(self.common)
  696.         for key in self.environ.keys():
  697.             if (isCGI_NAME(key) or key[:5] == 'HTTP_') and 
  698.                (not hide_key(key)):
  699.                     keys[key] = 1
  700.         n=0
  701.         while 1:
  702.             n=n+1
  703.             key = "URL%s" % n
  704.             if not self.has_key(key): break
  705.         n=0
  706.         while 1:
  707.             n=n+1
  708.             key = "BASE%s" % n
  709.             if not self.has_key(key): break
  710.         keys.update(self.other)
  711.         keys=keys.keys()
  712.         keys.sort()
  713.         return keys
  714.     def __str__(self):
  715.         result="<h3>form</h3><table>"
  716.         row='<tr valign="top" align="left"><th>%s</th><td>%s</td></tr>'
  717.         for k,v in self.form.items():
  718.             result=result + row % (k,v)
  719.         result=result+"</table><h3>cookies</h3><table>"
  720.         for k,v in self.cookies.items():
  721.             result=result + row % (k,v)
  722.         result=result+"</table><h3>other</h3><table>"
  723.         for k,v in self.other.items():
  724.             if k in ('PARENTS','RESPONSE'): continue
  725.             result=result + row % (k,v)
  726.     
  727.         for n in "0123456789":
  728.             key = "URL%s"%n
  729.             try: result=result + row % (key,self[key]) 
  730.             except KeyError: pass
  731.         for n in "0123456789":
  732.             key = "BASE%s"%n
  733.             try: result=result + row % (key,self[key]) 
  734.             except KeyError: pass
  735.         result=result+"</table><h3>environ</h3><table>"
  736.         for k,v in self.environ.items():
  737.             if not hide_key(k):
  738.                 result=result + row % (k,v)
  739.         return result+"</table>"
  740.     __repr__=__str__
  741.     def _authUserPW(self):
  742.         global base64
  743.         auth=self._auth
  744.         if auth:
  745.             if lower(auth[:6]) == 'basic ':
  746.                 if base64 is None: import base64
  747.                 [name,password] = split(
  748.                     base64.decodestring(split(auth)[-1]), ':')
  749.                 return name, password
  750. base64=None
  751. def sane_environment(env):
  752.     # return an environment mapping which has been cleaned of
  753.     # funny business such as REDIRECT_ prefixes added by Apache
  754.     # or HTTP_CGI_AUTHORIZATION hacks.
  755.     dict={}
  756.     for key, val in env.items():
  757.         while key[:9]=='REDIRECT_':
  758.             key=key[9:]
  759.         dict[key]=val
  760.     if dict.has_key('HTTP_CGI_AUTHORIZATION'):
  761.         dict['HTTP_AUTHORIZATION']=dict['HTTP_CGI_AUTHORIZATION']
  762.         try: del dict['HTTP_CGI_AUTHORIZATION']
  763.         except: pass
  764.     return dict
  765. def str_field(v):
  766.     if type(v) is ListType:
  767.         return map(str_field,v)
  768.     if hasattr(v,'__class__') and v.__class__ is FieldStorage:
  769.         v=v.value
  770.     elif type(v) is not StringType:
  771.         if hasattr(v,'file') and v.file: v=v.file
  772.         elif hasattr(v,'value'): v=v.value
  773.     return v
  774. class FileUpload:
  775.     '''
  776.     File upload objects
  777.     File upload objects are used to represent file-uploaded data.
  778.     File upload objects can be used just like files.
  779.     In addition, they have a 'headers' attribute that is a dictionary
  780.     containing the file-upload headers, and a 'filename' attribute
  781.     containing the name of the uploaded file.
  782.     '''
  783.     def __init__(self, aFieldStorage):
  784.         file=aFieldStorage.file
  785.         if hasattr(file, '__methods__'): methods=file.__methods__
  786.         else: methods= ['close', 'fileno', 'flush', 'isatty',
  787.                         'read', 'readline', 'readlines', 'seek',
  788.                         'tell', 'truncate', 'write', 'writelines']
  789.         d=self.__dict__
  790.         for m in methods:
  791.             if hasattr(file,m): d[m]=getattr(file,m)
  792.         self.headers=aFieldStorage.headers
  793.         self.filename=aFieldStorage.filename
  794.     
  795. parse_cookie_lock=allocate_lock()
  796. def parse_cookie(text,
  797.                  result=None,
  798.                  qparmre=regex.compile(
  799.                      '([- ]*'
  800.                      '([^- ;,="]+)="([^"]*)"'
  801.                      '([- ]*[;,])?[- ]*)'
  802.                      ),
  803.                  parmre=regex.compile(
  804.                      '([- ]*'
  805.                      '([^- ;,="]+)=([^- ;,"]*)'
  806.                      '([- ]*[;,])?[- ]*)'
  807.                      ),
  808.                  acquire=parse_cookie_lock.acquire,
  809.                  release=parse_cookie_lock.release,
  810.                  ):
  811.     if result is None: result={}
  812.     already_have=result.has_key
  813.     acquire()
  814.     try:
  815.         if qparmre.match(text) >= 0:
  816.             # Match quoted correct cookies
  817.             name=qparmre.group(2)
  818.             value=qparmre.group(3)
  819.             l=len(qparmre.group(1))
  820.         elif parmre.match(text) >= 0:
  821.             # Match evil MSIE cookies ;)
  822.             name=parmre.group(2)
  823.             value=parmre.group(3)
  824.             l=len(parmre.group(1))
  825.         else:
  826.             # this may be an invalid cookie.
  827.             # We'll simply bail without raising an error
  828.             # if the cookie is invalid.
  829.             return result
  830.             
  831.     finally: release()
  832.     if not already_have(name): result[name]=value
  833.     return apply(parse_cookie,(text[l:],result))
  834. # add class
  835. class record:
  836.     def __str__(self):
  837.         L1 = self.__dict__.items()
  838.         L1.sort()
  839.         return join(map(lambda item: "%s: %s" %item, L1), ", ") 
  840.     __repr__ = __str__
  841. # Flags
  842. SEQUENCE=1
  843. DEFAULT=2
  844. RECORD=4
  845. RECORDS=8
  846. REC=RECORD|RECORDS
  847. EMPTY=16
  848. CONVERTED=32