home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / xampp / xampp-python-addon-1.4.9-installer.exe / publisher.py < prev    next >
Encoding:
Python Source  |  2004-02-16  |  9.1 KB  |  276 lines

  1.  #
  2.  # Copyright 2004 Apache Software Foundation 
  3.  # 
  4.  # Licensed under the Apache License, Version 2.0 (the "License"); you
  5.  # may not use this file except in compliance with the License.  You
  6.  # may obtain a copy of the License at
  7.  #
  8.  #      http://www.apache.org/licenses/LICENSE-2.0
  9.  #
  10.  # Unless required by applicable law or agreed to in writing, software
  11.  # distributed under the License is distributed on an "AS IS" BASIS,
  12.  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  13.  # implied.  See the License for the specific language governing
  14.  # permissions and limitations under the License.
  15.  #
  16.  # Originally developed by Gregory Trubetskoy.
  17.  #
  18.  # $Id: publisher.py,v 1.36 2004/02/16 19:47:27 grisha Exp $
  19.  
  20. """
  21.   This handler is conceputally similar to Zope's ZPublisher, except
  22.   that it:
  23.  
  24.   1. Is written specifically for mod_python and is therefore much faster
  25.   2. Does not require objects to have a documentation string
  26.   3. Passes all arguments as simply string
  27.   4. Does not try to match Python errors to HTTP errors
  28.   5. Does not give special meaning to '.' and '..'.
  29. """
  30.  
  31. import apache
  32. import util
  33.  
  34. import sys
  35. import os
  36. import imp
  37. import re
  38. import base64
  39.  
  40. import new
  41. from types import *
  42.  
  43. imp_suffixes = " ".join([x[0][1:] for x in imp.get_suffixes()])
  44.  
  45. def handler(req):
  46.  
  47.     req.allow_methods(["GET", "POST"])
  48.     if req.method not in ["GET", "POST"]:
  49.         raise apache.SERVER_RETURN, apache.HTTP_METHOD_NOT_ALLOWED
  50.  
  51.     func_path = ""
  52.     if req.path_info:
  53.         func_path = req.path_info[1:] # skip first /
  54.         func_path = func_path.replace("/", ".")
  55.         if func_path[-1:] == ".":
  56.             func_path = func_path[:-1] 
  57.  
  58.     # default to 'index' if no path_info was given
  59.     if not func_path:
  60.         func_path = "index"
  61.  
  62.     # if any part of the path begins with "_", abort
  63.     if func_path[0] == '_' or func_path.count("._"):
  64.         raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
  65.  
  66.     ## import the script
  67.     path, module_name =  os.path.split(req.filename)
  68.     if not module_name:
  69.         module_name = "index"
  70.  
  71.     # get rid of the suffix
  72.     #   explanation: Suffixes that will get stripped off
  73.     #   are those that were specified as an argument to the
  74.     #   AddHandler directive. Everything else will be considered
  75.     #   a package.module rather than module.suffix
  76.     exts = req.get_addhandler_exts()
  77.     if not exts:
  78.         # this is SetHandler, make an exception for Python suffixes
  79.         exts = imp_suffixes
  80.     if req.extension:  # this exists if we're running in a | .ext handler
  81.         exts += req.extension[1:] 
  82.     if exts:
  83.         suffixes = exts.strip().split()
  84.         exp = "\\." + "$|\\.".join(suffixes)
  85.         suff_matcher = re.compile(exp) # python caches these, so its fast
  86.         module_name = suff_matcher.sub("", module_name)
  87.  
  88.     # import module (or reload if needed)
  89.     # the [path] argument tells import_module not to allow modules whose
  90.     # full path is not in [path] or below.
  91.     config = req.get_config()
  92.     autoreload=int(config.get("PythonAutoReload", 1))
  93.     log=int(config.get("PythonDebug", 0))
  94.     try:
  95.         module = apache.import_module(module_name,
  96.                                       autoreload=autoreload,
  97.                                       log=log,
  98.                                       path=[path])
  99.     except ImportError:
  100.         et, ev, etb = sys.exc_info()
  101.         # try again, using default module, perhaps this is a
  102.         # /directory/function (as opposed to /directory/module/function)
  103.         func_path = module_name
  104.         module_name = "index"
  105.         try:
  106.             module = apache.import_module(module_name,
  107.                                           autoreload=autoreload,
  108.                                           log=log,
  109.                                           path=[path])
  110.         except ImportError:
  111.             # raise the original exception
  112.             raise et, ev, etb
  113.         
  114.     # does it have an __auth__?
  115.     realm, user, passwd = process_auth(req, module)
  116.  
  117.     # resolve the object ('traverse')
  118.     try:
  119.         object = resolve_object(req, module, func_path, realm, user, passwd)
  120.     except AttributeError:
  121.         raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
  122.     
  123.     # not callable, a class or an unbound method
  124.     if (not callable(object) or 
  125.         type(object) is ClassType or
  126.         (hasattr(object, 'im_self') and not object.im_self)):
  127.  
  128.         result = str(object)
  129.         
  130.     else:
  131.         # callable, (but not a class or unbound method)
  132.         
  133.         # process input, if any
  134.         req.form = util.FieldStorage(req, keep_blank_values=1)
  135.         
  136.         result = util.apply_fs_data(object, req.form, req=req)
  137.  
  138.     if result or req.bytes_sent > 0 or req.next:
  139.         
  140.         if result is None:
  141.             result = ""
  142.         else:
  143.             result = str(result)
  144.  
  145.         # unless content_type was manually set, we will attempt
  146.         # to guess it
  147.         if not req._content_type_set:
  148.             # make an attempt to guess content-type
  149.             if result[:100].strip()[:6].lower() == '<html>' \
  150.                or result.find('</') > 0:
  151.                 req.content_type = 'text/html'
  152.             else:
  153.                 req.content_type = 'text/plain'
  154.  
  155.         if req.method != "HEAD":
  156.             req.write(result)
  157.         else:
  158.             req.write("")
  159.         return apache.OK
  160.     else:
  161.         req.log_error("mod_python.publisher: %s returned nothing." % `object`)
  162.         return apache.HTTP_INTERNAL_SERVER_ERROR
  163.  
  164. def process_auth(req, object, realm="unknown", user=None, passwd=None):
  165.  
  166.     found_auth, found_access = 0, 0
  167.  
  168.     # because ap_get_basic insists on making sure that AuthName and
  169.     # AuthType directives are specified and refuses to do anything
  170.     # otherwise (which is technically speaking a good thing), we
  171.     # have to do base64 decoding ourselves.
  172.     #
  173.     # to avoid needless header parsing, user and password are parsed
  174.     # once and the are received as arguments
  175.     if not user and req.headers_in.has_key("Authorization"):
  176.         try:
  177.             s = req.headers_in["Authorization"][6:]
  178.             s = base64.decodestring(s)
  179.             user, passwd = s.split(":", 1)
  180.         except:
  181.             raise apache.SERVER_RETURN, apache.HTTP_BAD_REQUEST
  182.  
  183.     if hasattr(object, "__auth_realm__"):
  184.         realm = object.__auth_realm__
  185.  
  186.     if type(object) is FunctionType:
  187.         # functions are a bit tricky
  188.  
  189.         if hasattr(object, "func_code"):
  190.             func_code = object.func_code
  191.  
  192.             if "__auth__" in func_code.co_names:
  193.                 i = list(func_code.co_names).index("__auth__")
  194.                 __auth__ = func_code.co_consts[i+1]
  195.                 if hasattr(__auth__, "co_name"):
  196.                     __auth__ = new.function(__auth__, globals())
  197.                 found_auth = 1
  198.  
  199.             if "__access__" in func_code.co_names:
  200.                 # first check the constant names
  201.                 i = list(func_code.co_names).index("__access__")
  202.                 __access__ = func_code.co_consts[i+1]
  203.                 if hasattr(__access__, "co_name"):
  204.                     __access__ = new.function(__access__, globals())
  205.                 found_access = 1
  206.  
  207.             if "__auth_realm__" in func_code.co_names:
  208.                 i = list(func_code.co_names).index("__auth_realm__")
  209.                 realm = func_code.co_consts[i+1]
  210.  
  211.     else:
  212.         if hasattr(object, "__auth__"):
  213.             __auth__ = object.__auth__
  214.             found_auth = 1
  215.         if hasattr(object, "__access__"):
  216.             __access__ = object.__access__
  217.             found_access = 1
  218.  
  219.     if found_auth:
  220.  
  221.         if not user:
  222.             # note that Opera supposedly doesn't like spaces around "=" below
  223.             s = 'Basic realm="%s"' % realm 
  224.             req.err_headers_out["WWW-Authenticate"] = s
  225.             raise apache.SERVER_RETURN, apache.HTTP_UNAUTHORIZED    
  226.  
  227.         if callable(__auth__):
  228.             rc = __auth__(req, user, passwd)
  229.         else:
  230.             if type(__auth__) is DictionaryType:
  231.                 rc = __auth__.has_key(user) and __auth__[user] == passwd
  232.             else:
  233.                 rc = __auth__
  234.             
  235.         if not rc:
  236.             s = 'Basic realm = "%s"' % realm
  237.             req.err_headers_out["WWW-Authenticate"] = s
  238.             raise apache.SERVER_RETURN, apache.HTTP_UNAUTHORIZED    
  239.  
  240.     if found_access:
  241.  
  242.         if callable(__access__):
  243.             rc = __access__(req, user)
  244.         else:
  245.             if type(__access__) in (ListType, TupleType):
  246.                 rc = user in __access__
  247.             else:
  248.                 rc = __access__
  249.  
  250.         if not rc:
  251.             raise apache.SERVER_RETURN, apache.HTTP_FORBIDDEN
  252.  
  253.     return realm, user, passwd
  254.  
  255. def resolve_object(req, obj, object_str, realm=None, user=None, passwd=None):
  256.     """
  257.     This function traverses the objects separated by .
  258.     (period) to find the last one we're looking for.
  259.     """
  260.  
  261.     for obj_str in  object_str.split('.'):
  262.         obj = getattr(obj, obj_str)
  263.  
  264.         # object cannot be a module
  265.         if type(obj) == ModuleType:
  266.             raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
  267.  
  268.         realm, user, passwd = process_auth(req, obj, realm,
  269.                                            user, passwd)
  270.  
  271.     return obj
  272.  
  273.     
  274.         
  275.  
  276.