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

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.16 $'[11:-2]
  65. from string import join, split, find, rfind, lower, upper
  66. from urllib import quote
  67. UNSPECIFIED_ROLES=''
  68. try:
  69.     from ExtensionClass import Base
  70.     class RequestContainer(Base):
  71.         def __init__(self,**kw):
  72.             for k,v in kw.items(): self.__dict__[k]=v
  73.         def manage_property_types(self):
  74.             return type_converters.keys()
  75.             
  76. except:
  77.     class RequestContainer:
  78.         def __init__(self,**kw):
  79.             for k,v in kw.items(): self.__dict__[k]=v
  80. _marker=[]
  81. class BaseRequest:
  82.     """Provide basic ZPublisher request management
  83.     
  84.     This object provides access to request data. Request data may
  85.     vary depending on the protocol used.
  86.     Request objects are created by the object publisher and will be
  87.     passed to published objects through the argument name, REQUEST.
  88.     The request object is a mapping object that represents a
  89.     collection of variable to value mappings.
  90.     """
  91.     _file=None
  92.     common={} # Common request data
  93.     _auth=None
  94.     _held=()
  95.     def __init__(self, other=None, **kw):
  96.         """The constructor is not allowed to raise errors
  97.         """
  98.         if other is None: other=kw
  99.         else: other.update(kw)
  100.         self.other=other
  101.     def close(self):
  102.         self.other.clear()
  103.         self._held=None
  104.     def processInputs(self):
  105.         """Do any input processing that could raise errors
  106.         """
  107.     def __len__(self):
  108.         return 1
  109.     def __setitem__(self,key,value):
  110.         """Set application variables
  111.         This method is used to set a variable in the requests "other"
  112.         category.
  113.         """
  114.         self.other[key]=value
  115.     set=__setitem__
  116.     def __getitem__(self,key,
  117.                     default=_marker, # Any special internal marker will do
  118.                     ):
  119.         """Get a variable value
  120.         Return a value for the required variable name.
  121.         The value will be looked up from one of the request data
  122.         categories. The search order is environment variables,
  123.         other variables, form data, and then cookies. 
  124.         
  125.         """
  126.         if key=='REQUEST': return self
  127.         v=self.other.get(key, _marker)
  128.         if v is not _marker: return v
  129.         v=self.common.get(key, default)
  130.         if v is not _marker: return v
  131.         if key=='BODY' and self._file is not None:
  132.             p=self._file.tell()
  133.             self._file.seek(0)
  134.             v=self._file.read()
  135.             self._file.seek(p)
  136.             self.other[key]=v
  137.             return v
  138.         if key=='BODYFILE' and self._file is not None:
  139.             v=self._file
  140.             self.other[key]=v
  141.             return v
  142.         
  143.         raise KeyError, key
  144.     __getattr__=get=__getitem__
  145.     def has_key(self,key):
  146.         return self.get(key, _marker) is not _marker
  147.     def keys(self):
  148.         keys = {}
  149.         keys.update(self.common)
  150.         keys.update(self.other)
  151.         return keys.keys()
  152.     def items(self):
  153.         result = []
  154.         get=self.get
  155.         for k in self.keys():
  156.             result.append((k, get(k)))
  157.         return result
  158.     def values(self):
  159.         result = []
  160.         get=self.get
  161.         for k in self.keys():
  162.             result.append(get(k))
  163.         return result
  164.     def __str__(self):
  165.         L1 = self.items()
  166.         L1.sort()
  167.         return join(map(lambda item: "%s:t%s" % item, L1), "n")
  168.     __repr__=__str__
  169.     def traverse(self, path, response=None):
  170.         """Traverse the object space
  171.         The REQUEST must already have a PARENTS item with at least one
  172.         object in it.  This is typically the root object.
  173.         """
  174.         request=self
  175.         request_get=request.get
  176.         if response is None: response=self.response
  177.         debug_mode=response.debug_mode
  178.         if path[:1] != '/': path='/'+path
  179.         if path[-1:] != '/': path=path+'/'
  180.         if find(path,'/.') >= 0:
  181.             path=join(split(path,'/./'),'/')
  182.             l=find(path,'/../',1)
  183.             while l > 0:
  184.                 p1=path[:l]
  185.                 path=path[:rfind(p1,'/')+1]+path[l+4:]
  186.                 l=find(path,'/../',1)
  187.         path=path[1:-1]
  188.     
  189.         path=split(path,'/')
  190.         while path and not path[0]: path = path[1:]
  191.     
  192.         method=req_method=upper(request_get('REQUEST_METHOD', 'GET'))
  193.         baseflag=0
  194.         if method=='GET' or method=='POST':
  195.             method='index_html'
  196.         else: baseflag=1
  197.         URL=request['URL']
  198.     
  199.         parents=request['PARENTS']
  200.         object=parents[-1]
  201.         del parents[:]
  202.         try:
  203.             # We build parents in the wrong order, so we
  204.             # need to make sure we reverse it when we're doe.
  205.             if hasattr(object,'__roles__'): roles=object.__roles__
  206.             else:                           roles=UNSPECIFIED_ROLES
  207.         
  208.             # if the top object has a __bobo_traverse__ method, then use it
  209.             # to possibly traverse to an alternate top-level object.
  210.             if hasattr(object,'__bobo_traverse__'):
  211.                 try: object=object.__bobo_traverse__(request)
  212.                 except: pass            
  213.         
  214.             # Get default object if no path was specified:
  215.             if not path:
  216.                 if not method: return response.forbiddenError(entry_name)
  217.                 entry_name=method
  218.                 try:
  219.                     if hasattr(object,entry_name):
  220.                         response.setBase(URL)
  221.                         path=[entry_name]
  222.                     else:
  223.                         try:
  224.                             if object.has_key(entry_name):
  225.                                 path=[entry_name]
  226.                         except: pass
  227.                 except: pass
  228.         
  229.             # Traverse the URL to find the object:
  230.         
  231.             if hasattr(object, '__of__'): 
  232.                 # Try to bind the top-level object to the request
  233.                 object=object.__of__(RequestContainer(REQUEST=request))
  234.         
  235.             steps=self.steps
  236.             path.reverse()
  237.             while path:
  238.                 entry_name=path[-1]
  239.                 del path[-1]
  240.                 URL="%s/%s" % (URL,quote(entry_name))
  241.                 got=0
  242.                 if entry_name:
  243.                     if entry_name[:1]=='_':
  244.                         if debug_mode:
  245.                             return response.debugError(
  246.                                 "Object name begins with an underscore at: %s"
  247.                                 % URL)
  248.                         else: return response.forbiddenError(entry_name)
  249.         
  250.                     if hasattr(object,'__bobo_traverse__'):
  251.                         request['URL']=URL
  252.                         subobject=object.__bobo_traverse__(request,entry_name)
  253.                         if type(subobject) is type(()) and len(subobject) > 1:
  254.                             while len(subobject) > 2:
  255.                                 parents.append(subobject[0])
  256.                                 subobject=subobject[1:]
  257.                             object, subobject = subobject
  258.                     else:
  259.                         try:
  260.                             
  261.                             # Note - this is necessary to support
  262.                             # things like DAV.  We have to make sure
  263.                             # that the target object is not acquired
  264.                             # if the request_method is other than GET
  265.                             # or POST. Otherwise, you could never use
  266.                             # PUT to add a new object named 'test' if
  267.                             # an object 'test' existed above it in the
  268.                             # heirarchy -- you'd always get the
  269.                             # existing object :(
  270.                             
  271.                             if baseflag and hasattr(object, 'aq_base'):
  272.                                 if hasattr(object.aq_base, entry_name):
  273.                                     subobject=getattr(object, entry_name)
  274.                                 else: raise AttributeError, entry_name
  275.                             else: subobject=getattr(object, entry_name)
  276.                         except AttributeError:
  277.                             got=1
  278.                             try: subobject=object[entry_name]
  279.                             except (KeyError, IndexError,
  280.                                     TypeError, AttributeError):
  281.                                 if debug_mode:
  282.                                     return response.debugError(
  283.                                         "Cannot locate object at: %s" %URL) 
  284.                                 else: return response.notFoundError(URL)
  285.         
  286.                     try:
  287.                         try: doc=subobject.__doc__
  288.                         except: doc=getattr(object, entry_name+'__doc__')
  289.                         if not doc: raise AttributeError, entry_name
  290.                     except:
  291.                         if debug_mode:
  292.                             return response.debugError(
  293.                                 "Missing doc string at: %s" % URL)
  294.                         else: return response.notFoundError("%s" % (URL))
  295.     
  296.                     if hasattr(subobject,'__roles__'):
  297.                         roles=subobject.__roles__
  298.                     else:
  299.                         if not got:
  300.                             roleshack=entry_name+'__roles__'
  301.                             if hasattr(object, roleshack):
  302.                                 roles=getattr(object, roleshack)
  303.     
  304.                     # Promote subobject to object
  305.                 
  306.                     parents.append(object)
  307.                     object=subobject
  308.     
  309.                     steps.append(entry_name)
  310.         
  311.                     # Check for method:
  312.                     if not path:
  313.                         if (method and hasattr(object,method)
  314.                             and entry_name != method
  315.                             and getattr(object, method) is not None
  316.                             ):
  317.                             request._hacked_path=1
  318.                             path=[method]
  319.                         else:
  320.                             if (hasattr(object, '__call__') and
  321.                                 hasattr(object.__call__,'__roles__')):
  322.                                 roles=object.__call__.__roles__
  323.                             if request._hacked_path:
  324.                                 i=rfind(URL,'/')
  325.                                 if i > 0: response.setBase(URL[:i])
  326.         
  327.         finally:
  328.             # We need to MAKE SURE this happens due to new error handling
  329.             parents.reverse()
  330.     
  331.         # Do authorization checks
  332.         user=groups=None
  333.         i=0
  334.         if roles is not None:
  335.     
  336.             last_parent_index=len(parents)
  337.             if hasattr(object, '__allow_groups__'):
  338.                 groups=object.__allow_groups__
  339.                 inext=0
  340.             else:
  341.                 inext=None
  342.                 for i in range(last_parent_index):
  343.                     if hasattr(parents[i],'__allow_groups__'):
  344.                         groups=parents[i].__allow_groups__
  345.                         inext=i+1
  346.                         break
  347.     
  348.             if inext is not None:
  349.                 i=inext
  350.     
  351.                 if hasattr(groups, 'validate'): v=groups.validate
  352.                 else: v=old_validation
  353.     
  354.                 auth=request._auth
  355.     
  356.                 if v is old_validation and roles is UNSPECIFIED_ROLES:
  357.                     # No roles, so if we have a named group, get roles from
  358.                     # group keys
  359.                     if hasattr(groups,'keys'): roles=groups.keys()
  360.                     else:
  361.                         try: groups=groups()
  362.                         except: pass
  363.                         try: roles=groups.keys()
  364.                         except: pass
  365.     
  366.                     if groups is None:
  367.                         # Public group, hack structures to get it to validate
  368.                         roles=None
  369.                         auth=''
  370.     
  371.                 if v is old_validation:
  372.                     user=old_validation(groups, request, auth, roles)
  373.                 elif roles is UNSPECIFIED_ROLES: user=v(request, auth)
  374.                 else: user=v(request, auth, roles)
  375.     
  376.                 while user is None and i < last_parent_index:
  377.                     parent=parents[i]
  378.                     i=i+1
  379.                     if hasattr(parent, '__allow_groups__'): 
  380.                         groups=parent.__allow_groups__
  381.                     else: continue
  382.                     if hasattr(groups,'validate'): v=groups.validate
  383.                     else: v=old_validation
  384.                     if v is old_validation:
  385.                         user=old_validation(groups, request, auth, roles)
  386.                     elif roles is UNSPECIFIED_ROLES: user=v(request, auth)
  387.                     else: user=v(request, auth, roles)
  388.                     
  389.             if user is None and roles != UNSPECIFIED_ROLES:
  390.                 response.unauthorized()
  391.     
  392.         steps=join(steps[:-i],'/')
  393.         if user is not None:
  394.             request['AUTHENTICATED_USER']=user
  395.             request['AUTHENTICATION_PATH']=steps
  396.         # Remove http request method from the URL.
  397.         request['URL']=URL
  398.     
  399.         return object
  400.     retry_count=0
  401.     def supports_retry(self): return 0
  402.     def _hold(self, object):
  403.         """Hold a reference to an object to delay it's destruction until mine
  404.         """
  405.         self._held=self._held+(object,)
  406.     
  407. def old_validation(groups, request, auth,
  408.                    roles=UNSPECIFIED_ROLES):
  409.     if auth:
  410.         auth=request._authUserPW()
  411.         if auth: name,password = auth
  412.         elif roles is None: return ''
  413.         else: return None
  414.     elif request.environ.has_key('REMOTE_USER'):
  415.         name=request.environ['REMOTE_USER']
  416.         password=None
  417.     else:
  418.         if roles is None: return ''
  419.         return None
  420.     if roles is None: return name
  421.     keys=None
  422.     try:
  423.         keys=groups.keys
  424.     except:
  425.         try:
  426.             groups=groups() # Maybe it was a method defining a group
  427.             keys=groups.keys
  428.         except: pass
  429.     if keys is not None:
  430.         # OK, we have a named group, so apply the roles to the named
  431.         # group.
  432.         if roles is UNSPECIFIED_ROLES: roles=keys()
  433.         g=[]
  434.         for role in roles:
  435.             if groups.has_key(role): g.append(groups[role])
  436.         groups=g
  437.     for d in groups:
  438.         if d.has_key(name) and (d[name]==password or password is None):
  439.             return name
  440.     if keys is None:
  441.         # Not a named group, so don't go further
  442.         raise 'Forbidden', (
  443.             """<strong>You are not authorized to access this resource""")
  444.     return None