PyXR

c:\python24\lib\site-packages \ isapi \ install.py



0001 """Installation utilities for Python ISAPI filters and extensions."""
0002 
0003 # this code adapted from "Tomcat JK2 ISAPI redirector", part of Apache
0004 # Created July 2004, Mark Hammond.
0005 import sys, os, imp, shutil, stat
0006 from win32com.client import GetObject, Dispatch
0007 from win32com.client.gencache import EnsureModule, EnsureDispatch
0008 import pythoncom
0009 import winerror
0010 import traceback
0011 
0012 _APP_INPROC  = 0
0013 _APP_OUTPROC = 1
0014 _APP_POOLED  = 2
0015 _IIS_OBJECT  = "IIS://LocalHost/W3SVC"
0016 _IIS_SERVER  = "IIsWebServer"
0017 _IIS_WEBDIR  = "IIsWebDirectory"
0018 _IIS_WEBVIRTUALDIR  = "IIsWebVirtualDir"
0019 _IIS_FILTERS = "IIsFilters"
0020 _IIS_FILTER  = "IIsFilter"
0021 
0022 _DEFAULT_SERVER_NAME = "Default Web Site"
0023 _DEFAULT_HEADERS     = "X-Powered-By: Python"
0024 _DEFAULT_PROTECTION  = _APP_POOLED
0025 
0026 # Default is for 'execute' only access - ie, only the extension
0027 # can be used.  This can be overridden via your install script.
0028 _DEFAULT_ACCESS_EXECUTE = True
0029 _DEFAULT_ACCESS_READ = False
0030 _DEFAULT_ACCESS_WRITE = False
0031 _DEFAULT_ACCESS_SCRIPT = False
0032 _DEFAULT_CONTENT_INDEXED = False
0033 _DEFAULT_ENABLE_DIR_BROWSING = False
0034 _DEFAULT_ENABLE_DEFAULT_DOC = False
0035 
0036 is_debug_build = False
0037 for imp_ext, _, _ in imp.get_suffixes():
0038     if imp_ext == "_d.pyd":
0039         is_debug_build = True
0040         break
0041 
0042 this_dir = os.path.abspath(os.path.dirname(__file__))
0043 
0044 class FilterParameters:
0045     Name = None
0046     Description = None
0047     Path = None
0048     Server = None
0049     def __init__(self, **kw):
0050         self.__dict__.update(kw)
0051 
0052 class VirtualDirParameters:
0053     Name = None # Must be provided.
0054     Description = None # defaults to Name
0055     AppProtection = _DEFAULT_PROTECTION
0056     Headers       = _DEFAULT_HEADERS
0057     Path          = None # defaults to WWW root.
0058     AccessExecute  = _DEFAULT_ACCESS_EXECUTE
0059     AccessRead     = _DEFAULT_ACCESS_READ
0060     AccessWrite    = _DEFAULT_ACCESS_WRITE
0061     AccessScript   = _DEFAULT_ACCESS_SCRIPT
0062     ContentIndexed = _DEFAULT_CONTENT_INDEXED
0063     EnableDirBrowsing = _DEFAULT_ENABLE_DIR_BROWSING
0064     EnableDefaultDoc  = _DEFAULT_ENABLE_DEFAULT_DOC
0065     ScriptMaps       = []
0066     ScriptMapUpdate = "end" # can be 'start', 'end', 'replace'
0067     Server = None
0068     def __init__(self, **kw):
0069         self.__dict__.update(kw)
0070 
0071 class ScriptMapParams:
0072     Extension = None
0073     Module = None
0074     Flags = 5
0075     Verbs = ""
0076     def __init__(self, **kw):
0077         self.__dict__.update(kw)
0078 
0079 class ISAPIParameters:
0080     ServerName     = _DEFAULT_SERVER_NAME
0081     # Description = None
0082     Filters = []
0083     VirtualDirs = []
0084     def __init__(self, **kw):
0085         self.__dict__.update(kw)
0086 
0087 verbose = 1 # The level - 0 is quiet.
0088 def log(level, what):
0089     if verbose >= level:
0090         print what
0091 
0092 # Convert an ADSI COM exception to the Win32 error code embedded in it.
0093 def _GetWin32ErrorCode(com_exc):
0094     hr, msg, exc, narg = com_exc
0095     # If we have more details in the 'exc' struct, use it.
0096     if exc:
0097         hr = exc[-1]
0098     if winerror.HRESULT_FACILITY(hr) != winerror.FACILITY_WIN32:
0099         raise
0100     return winerror.SCODE_CODE(hr)
0101 
0102 class InstallationError(Exception): pass
0103 class ItemNotFound(InstallationError): pass
0104 class ConfigurationError(InstallationError): pass
0105 
0106 def FindPath(options, server, name):
0107     if name.lower().startswith("iis://"):
0108         return name
0109     else:
0110         if name and name[0] != "/":
0111             name = "/"+name
0112         return FindWebServer(options, server)+"/ROOT"+name
0113 
0114 def FindWebServer(options, server_desc):
0115     # command-line options get first go.
0116     if options.server:
0117         server = options.server
0118     # If the config passed by the caller doesn't specify one, use the default
0119     elif server_desc is None:
0120         server = _IIS_OBJECT+"/1"
0121     else:
0122         server = server_desc
0123     # Check it is good.
0124     try:
0125         GetObject(server)
0126     except pythoncom.com_error, details:
0127         hr, msg, exc, arg_err = details
0128         if exc and exc[2]:
0129             msg = exc[2]
0130         raise ItemNotFound, \
0131               "WebServer %s: %s" % (server, msg)
0132     return server
0133 
0134 def CreateDirectory(params, options):
0135     if not params.Name:
0136         raise ConfigurationError, "No Name param"
0137     slash = params.Name.rfind("/")
0138     if slash >= 0:
0139         parent = params.Name[:slash]
0140         name = params.Name[slash+1:]
0141     else:
0142         parent = ""
0143         name = params.Name
0144     webDir = GetObject(FindPath(options, params.Server, parent))
0145     if parent:
0146         # Note that the directory won't be visible in the IIS UI
0147         # unless the directory exists on the filesystem.
0148         keyType = _IIS_WEBDIR
0149     else:
0150         keyType = _IIS_WEBVIRTUALDIR
0151     _CallHook(params, "PreInstall", options)
0152     try:
0153         newDir = webDir.Create(keyType, name)
0154     except pythoncom.com_error, details:
0155         rc = _GetWin32ErrorCode(details)
0156         if rc != winerror.ERROR_ALREADY_EXISTS:
0157             raise
0158         newDir = GetObject(FindPath(options, params.Server, params.Name))
0159         log(2, "Updating existing directory '%s'..." % (params.Name,))
0160     else:
0161         log(2, "Creating new directory '%s'..." % (params.Name,))
0162         
0163         friendly = params.Description or params.Name
0164         newDir.AppFriendlyName = friendly
0165         try:
0166             path = params.Path or webDir.Path
0167             newDir.Path = path
0168         except AttributeError:
0169             pass
0170         newDir.AppCreate2(params.AppProtection)
0171         newDir.HttpCustomHeaders = params.Headers
0172 
0173     log(2, "Setting directory options...")
0174     newDir.AccessExecute  = params.AccessExecute
0175     newDir.AccessRead     = params.AccessRead
0176     newDir.AccessWrite    = params.AccessWrite
0177     newDir.AccessScript   = params.AccessScript
0178     newDir.ContentIndexed = params.ContentIndexed
0179     newDir.EnableDirBrowsing = params.EnableDirBrowsing
0180     newDir.EnableDefaultDoc  = params.EnableDefaultDoc
0181     newDir.SetInfo()
0182     smp_items = []
0183     for smp in params.ScriptMaps:
0184         item = "%s,%s,%s" % (smp.Extension, smp.Module, smp.Flags)
0185         # IIS gets upset if there is a trailing verb comma, but no verbs
0186         if smp.Verbs:
0187             item += "," + smp.Verbs
0188         smp_items.append(item)
0189     if params.ScriptMapUpdate == "replace":
0190         newDir.ScriptMaps = smp_items
0191     elif params.ScriptMapUpdate == "end":
0192         for item in smp_items:
0193             if item not in newDir.ScriptMaps:
0194                 newDir.ScriptMaps = newDir.ScriptMaps + (item,)
0195     elif params.ScriptMapUpdate == "start":
0196         for item in smp_items:
0197             if item not in newDir.ScriptMaps:
0198                 newDir.ScriptMaps = (item,) + newDir.ScriptMaps
0199     else:
0200         raise ConfigurationError, \
0201               "Unknown ScriptMapUpdate option '%s'" % (params.ScriptMapUpdate,)
0202     newDir.SetInfo()
0203     _CallHook(params, "PostInstall", options, newDir)
0204     log(1, "Configured Virtual Directory: %s" % (params.Name,))
0205     return newDir
0206 
0207 def CreateISAPIFilter(filterParams, options):
0208     server = FindWebServer(options, filterParams.Server)
0209     _CallHook(filterParams, "PreInstall", options)
0210     try:
0211         filters = GetObject(server+"/Filters")
0212     except pythoncom.com_error, (hr, msg, exc, arg):
0213         # Brand new sites don't have the '/Filters' collection - create it.
0214         # Any errors other than 'not found' we shouldn't ignore.
0215         if winerror.HRESULT_FACILITY(hr) != winerror.FACILITY_WIN32 or \
0216            winerror.HRESULT_CODE(hr) != winerror.ERROR_PATH_NOT_FOUND:
0217             raise
0218         server_ob = GetObject(server)
0219         filters = server_ob.Create(_IIS_FILTERS, "Filters")
0220         filters.FilterLoadOrder = ""
0221         filters.SetInfo()
0222     try:
0223         newFilter = filters.Create(_IIS_FILTER, filterParams.Name)
0224         log(2, "Created new ISAPI filter...")
0225     except pythoncom.com_error, (hr, msg, exc, narg):
0226         if exc is None or exc[-1]!=-2147024713:
0227             raise
0228         log(2, "Updating existing filter '%s'..." % (filterParams.Name,))
0229         newFilter = GetObject(server+"/Filters/"+filterParams.Name)
0230     assert os.path.isfile(filterParams.Path)
0231     newFilter.FilterPath  = filterParams.Path
0232     newFilter.FilterDescription = filterParams.Description
0233     newFilter.SetInfo()
0234     load_order = [b.strip() for b in filters.FilterLoadOrder.split(",")]
0235     if filterParams.Name not in load_order:
0236         load_order.append(filterParams.Name)
0237         filters.FilterLoadOrder = ",".join(load_order)
0238         filters.SetInfo()
0239     _CallHook(filterParams, "PostInstall", options, newFilter)
0240     log (1, "Configured Filter: %s" % (filterParams.Name,))
0241     return newFilter
0242 
0243 def DeleteISAPIFilter(filterParams, options):
0244     _CallHook(filterParams, "PreRemove", options)
0245     server = FindWebServer(options, filterParams.Server)
0246     filters = GetObject(server+"/Filters")
0247     try:
0248         filters.Delete(_IIS_FILTER, filterParams.Name)
0249         log(2, "Deleted ISAPI filter '%s'" % (filterParams.Name,))
0250     except pythoncom.com_error, details:
0251         rc = _GetWin32ErrorCode(details)
0252         if rc != winerror.ERROR_PATH_NOT_FOUND:
0253             raise
0254         log(2, "ISAPI filter '%s' did not exist." % (filterParams.Name,))
0255     if filterParams.Path:
0256         load_order = [b.strip() for b in filters.FilterLoadOrder.split(",")]
0257         if filterParams.Path in load_order:
0258             load_order.remove(filterParams.Path)
0259             filters.FilterLoadOrder = ",".join(load_order)
0260             filters.SetInfo()
0261     _CallHook(filterParams, "PostRemove", options)
0262     log (1, "Deleted Filter: %s" % (filterParams.Name,))
0263 
0264 def CheckLoaderModule(dll_name):
0265     suffix = ""
0266     if is_debug_build: suffix = "_d"
0267     template = os.path.join(this_dir,
0268                             "PyISAPI_loader" + suffix + ".dll")
0269     if not os.path.isfile(template):
0270         raise ConfigurationError, \
0271               "Template loader '%s' does not exist" % (template,)
0272     # We can't do a simple "is newer" check, as the DLL is specific to the
0273     # Python version.  So we check the date-time and size are identical,
0274     # and skip the copy in that case.
0275     src_stat = os.stat(template)
0276     try:
0277         dest_stat = os.stat(dll_name)
0278     except os.error:
0279         same = 0
0280     else:
0281         same = src_stat[stat.ST_SIZE]==dest_stat[stat.ST_SIZE] and \
0282                src_stat[stat.ST_MTIME]==dest_stat[stat.ST_MTIME]
0283     if not same:
0284         log(2, "Updating %s->%s" % (template, dll_name))
0285         shutil.copyfile(template, dll_name)
0286         shutil.copystat(template, dll_name)
0287     else:
0288         log(2, "%s is up to date." % (dll_name,))
0289 
0290 def _CallHook(ob, hook_name, options, *extra_args):
0291     func = getattr(ob, hook_name, None)
0292     if func is not None:
0293         args = (ob,options) + extra_args
0294         func(*args)
0295 
0296 def Install(params, options):
0297     _CallHook(params, "PreInstall", options)
0298     for vd in params.VirtualDirs:
0299         CreateDirectory(vd, options)
0300         
0301     for filter_def in params.Filters:
0302         CreateISAPIFilter(filter_def, options)
0303     _CallHook(params, "PostInstall", options)
0304 
0305 def Uninstall(params, options):
0306     _CallHook(params, "PreRemove", options)
0307     for vd in params.VirtualDirs:
0308         _CallHook(vd, "PreRemove", options)
0309         try:
0310             directory = GetObject(FindPath(options, vd.Server, vd.Name))
0311             directory.AppUnload()
0312             parent = GetObject(directory.Parent)
0313             parent.Delete(directory.Class, directory.Name)
0314         except pythoncom.com_error, details:
0315             rc = _GetWin32ErrorCode(details)
0316             if rc != winerror.ERROR_PATH_NOT_FOUND:
0317                 raise
0318         _CallHook(vd, "PostRemove", options)
0319         log (1, "Deleted Virtual Directory: %s" % (vd.Name,))
0320 
0321     for filter_def in params.Filters:
0322         DeleteISAPIFilter(filter_def, options)
0323     _CallHook(params, "PostRemove", options)
0324 
0325 # Patch up any missing module names in the params, replacing them with
0326 # the DLL name that hosts this extension/filter.
0327 def _PatchParamsModule(params, dll_name, file_must_exist = True):
0328     if file_must_exist:
0329         if not os.path.isfile(dll_name):
0330             raise ConfigurationError, "%s does not exist" % (dll_name,)
0331 
0332     # Patch up all references to the DLL.
0333     for f in params.Filters:
0334         if f.Path is None: f.Path = dll_name
0335     for d in params.VirtualDirs:
0336         for sm in d.ScriptMaps:
0337             if sm.Module is None: sm.Module = dll_name
0338 
0339 def GetLoaderModuleName(mod_name):
0340     # find the name of the DLL hosting us.
0341     # By default, this is "_{module_base_name}.dll"
0342     if hasattr(sys, "frozen"):
0343         # What to do?  The .dll knows its name, but this is likely to be
0344         # executed via a .exe, which does not know.
0345         base, ext = os.path.splitext(mod_name)
0346         path, base = os.path.split(base)
0347         # handle the common case of 'foo.exe'/'foow.exe'
0348         if base.endswith('w'):
0349             base = base[:-1]
0350         # For py2exe, we have '_foo.dll' as the standard pyisapi loader - but
0351         # 'foo.dll' is what we use (it just delegates).
0352         # So no leading '_' on the installed name.
0353         dll_name = os.path.abspath(os.path.join(path, base + ".dll"))
0354     else:
0355         base, ext = os.path.splitext(mod_name)
0356         path, base = os.path.split(base)
0357         dll_name = os.path.abspath(os.path.join(path, "_" + base + ".dll"))
0358     # Check we actually have it.
0359     if not hasattr(sys, "frozen"):
0360         CheckLoaderModule(dll_name)
0361     return dll_name
0362 
0363 def InstallModule(conf_module_name, params, options):
0364     if not hasattr(sys, "frozen"):
0365         conf_module_name = os.path.abspath(conf_module_name)
0366         if not os.path.isfile(conf_module_name):
0367             raise ConfigurationError, "%s does not exist" % (conf_module_name,)
0368 
0369     loader_dll = GetLoaderModuleName(conf_module_name)
0370     _PatchParamsModule(params, loader_dll)
0371     Install(params, options)
0372 
0373 def UninstallModule(conf_module_name, params, options):
0374     loader_dll = GetLoaderModuleName(conf_module_name)
0375     _PatchParamsModule(params, loader_dll, False)
0376     Uninstall(params, options)
0377 
0378 standard_arguments = {
0379     "install" : "Install the extension",
0380     "remove"  : "Remove the extension"
0381 }
0382 
0383 # We support 2 ways of extending our command-line/install support.
0384 # * Many of the installation items allow you to specify "PreInstall",
0385 #   "PostInstall", "PreRemove" and "PostRemove" hooks
0386 #   All hooks are called with the 'params' object being operated on, and
0387 #   the 'optparser' options for this session (ie, the command-line options)
0388 #   PostInstall for VirtualDirectories and Filters both have an additional
0389 #   param - the ADSI object just created.
0390 # * You can pass your own option parser for us to use, and/or define a map
0391 #   with your own custom arg handlers.  It is a map of 'arg'->function.
0392 #   The function is called with (options, log_fn, arg).  The function's
0393 #   docstring is used in the usage output.
0394 def HandleCommandLine(params, argv=None, conf_module_name = None,
0395                       default_arg = "install",
0396                       opt_parser = None, custom_arg_handlers = {}):
0397     """Perform installation or removal of an ISAPI filter or extension.
0398     
0399     This module handles standard command-line options and configuration
0400     information, and installs, removes or updates the configuration of an
0401     ISAPI filter or extension.
0402     
0403     You must pass your configuration information in params - all other
0404     arguments are optional, and allow you to configure the installation
0405     process.
0406     """
0407     global verbose
0408     from optparse import OptionParser
0409 
0410     argv = argv or sys.argv
0411     conf_module_name = conf_module_name or sys.argv[0]
0412     if opt_parser is None:
0413         # Build our own parser.
0414         parser = OptionParser(usage='')
0415     else:
0416         # The caller is providing their own filter, presumably with their
0417         # own options all setup.
0418         parser = opt_parser
0419 
0420     # build a usage string if we don't have one.
0421     if not parser.get_usage():
0422         all_args = standard_arguments.copy()
0423         for arg, handler in custom_arg_handlers.items():
0424             all_args[arg] = handler.__doc__
0425         arg_names = "|".join(all_args.keys())
0426         usage_string = "%prog [options] [" + arg_names + "]\n"
0427         usage_string += "commands:\n"
0428         for arg, desc in all_args.items():
0429             usage_string += " %-10s: %s" % (arg, desc) + "\n"
0430         parser.set_usage(usage_string[:-1])
0431 
0432     parser.add_option("-q", "--quiet",
0433                       action="store_false", dest="verbose", default=True,
0434                       help="don't print status messages to stdout")
0435     parser.add_option("-v", "--verbosity", action="count",
0436                       dest="verbose", default=1,
0437                       help="increase the verbosity of status messages")
0438     parser.add_option("", "--server", action="store",
0439                       help="Specifies the IIS server to install/uninstall on." \
0440                            " Default is '%s/1'" % (_IIS_OBJECT,))
0441 
0442     (options, args) = parser.parse_args(argv[1:])
0443     verbose = options.verbose
0444     if not args:
0445         args = [default_arg]
0446     try:
0447         for arg in args:
0448             if arg == "install":
0449                 InstallModule(conf_module_name, params, options)
0450                 log(1, "Installation complete.")
0451             elif arg in ["remove", "uninstall"]:
0452                 UninstallModule(conf_module_name, params, options)
0453                 log(1, "Uninstallation complete.")
0454             else:
0455                 handler = custom_arg_handlers.get(arg, None)
0456                 if handler is None:
0457                     parser.error("Invalid arg '%s'" % (arg,))
0458                 handler(options, log, arg)
0459     except (ItemNotFound, InstallationError), details:
0460         if options.verbose > 1:
0461             traceback.print_exc()
0462         print "%s: %s" % (details.__class__.__name__, details)
0463 

Generated by PyXR 0.9.4
SourceForge.net Logo