pywin32-checkins Mailing List for Python for Windows Extensions (Page 124)
OLD project page for the Python extensions for Windows
Brought to you by:
mhammond
You can subscribe to this list here.
2003 |
Jan
|
Feb
|
Mar
|
Apr
(2) |
May
(1) |
Jun
(6) |
Jul
(50) |
Aug
(11) |
Sep
(24) |
Oct
(184) |
Nov
(118) |
Dec
(22) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2004 |
Jan
(31) |
Feb
(25) |
Mar
(34) |
Apr
(105) |
May
(49) |
Jun
(38) |
Jul
(39) |
Aug
(7) |
Sep
(98) |
Oct
(79) |
Nov
(20) |
Dec
(17) |
2005 |
Jan
(66) |
Feb
(32) |
Mar
(43) |
Apr
(30) |
May
(58) |
Jun
(30) |
Jul
(16) |
Aug
(4) |
Sep
(21) |
Oct
(42) |
Nov
(11) |
Dec
(14) |
2006 |
Jan
(42) |
Feb
(30) |
Mar
(22) |
Apr
(1) |
May
(9) |
Jun
(15) |
Jul
(20) |
Aug
(9) |
Sep
(8) |
Oct
(1) |
Nov
(9) |
Dec
(43) |
2007 |
Jan
(52) |
Feb
(45) |
Mar
(20) |
Apr
(12) |
May
(59) |
Jun
(39) |
Jul
(35) |
Aug
(31) |
Sep
(17) |
Oct
(20) |
Nov
(4) |
Dec
(4) |
2008 |
Jan
(28) |
Feb
(111) |
Mar
(4) |
Apr
(27) |
May
(40) |
Jun
(27) |
Jul
(32) |
Aug
(94) |
Sep
(87) |
Oct
(153) |
Nov
(336) |
Dec
(331) |
2009 |
Jan
(298) |
Feb
(127) |
Mar
(20) |
Apr
(8) |
May
|
Jun
(10) |
Jul
(6) |
Aug
|
Sep
(2) |
Oct
(2) |
Nov
|
Dec
(1) |
2010 |
Jan
(7) |
Feb
(1) |
Mar
|
Apr
|
May
(15) |
Jun
(4) |
Jul
(3) |
Aug
(28) |
Sep
(1) |
Oct
(19) |
Nov
(16) |
Dec
(6) |
2011 |
Jan
(2) |
Feb
(18) |
Mar
(17) |
Apr
(12) |
May
(5) |
Jun
(11) |
Jul
(7) |
Aug
(2) |
Sep
(2) |
Oct
(4) |
Nov
(4) |
Dec
|
2012 |
Jan
(6) |
Feb
(2) |
Mar
|
Apr
(8) |
May
(4) |
Jun
(3) |
Jul
(13) |
Aug
(27) |
Sep
(8) |
Oct
(9) |
Nov
(3) |
Dec
(2) |
2013 |
Jan
|
Feb
(1) |
Mar
(5) |
Apr
(10) |
May
|
Jun
(2) |
Jul
|
Aug
|
Sep
|
Oct
|
Nov
(1) |
Dec
(9) |
2014 |
Jan
(2) |
Feb
(4) |
Mar
(4) |
Apr
(1) |
May
(4) |
Jun
(2) |
Jul
|
Aug
|
Sep
|
Oct
(2) |
Nov
|
Dec
(1) |
2015 |
Jan
(1) |
Feb
|
Mar
|
Apr
(6) |
May
(2) |
Jun
|
Jul
|
Aug
(1) |
Sep
|
Oct
|
Nov
|
Dec
|
2016 |
Jan
(3) |
Feb
(2) |
Mar
|
Apr
(3) |
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
2017 |
Jan
|
Feb
|
Mar
(1) |
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
(1) |
Oct
|
Nov
|
Dec
|
From: Mark H. <mha...@us...> - 2004-10-06 05:21:46
|
Update of /cvsroot/pywin32/pywin32/dotnet/docs In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv18710/dotnet/docs Removed Files: Language.html Python for .NET Paper.doc PythonCompiler.html building.html introduction.html Log Message: Given IronPython etc, I think we can consider this compiler dead (and can get at it from the attic if necessary) --- introduction.html DELETED --- --- building.html DELETED --- --- PythonCompiler.html DELETED --- --- Language.html DELETED --- --- Python for .NET Paper.doc DELETED --- |
From: Mark H. <mha...@us...> - 2004-10-06 05:21:46
|
Update of /cvsroot/pywin32/pywin32/dotnet/compiler/py2il In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv18710/dotnet/compiler/py2il Removed Files: __init__.py funcsigs.py gen_namespaces.py gencode.py genil.py genil_con.py options.py Log Message: Given IronPython etc, I think we can consider this compiler dead (and can get at it from the attic if necessary) --- funcsigs.py DELETED --- --- gencode.py DELETED --- --- options.py DELETED --- --- genil.py DELETED --- --- gen_namespaces.py DELETED --- --- __init__.py DELETED --- --- genil_con.py DELETED --- |
Update of /cvsroot/pywin32/pywin32/dotnet/compiler/compiler In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv18710/dotnet/compiler/compiler Removed Files: COPYRIGHT __init__.py ast.py consts.py misc.py pyassem.py pycodegen.py transformer.py visitor.py Log Message: Given IronPython etc, I think we can consider this compiler dead (and can get at it from the attic if necessary) --- __init__.py DELETED --- --- COPYRIGHT DELETED --- --- visitor.py DELETED --- --- misc.py DELETED --- --- transformer.py DELETED --- --- pycodegen.py DELETED --- --- ast.py DELETED --- --- consts.py DELETED --- --- pyassem.py DELETED --- |
From: Mark H. <mha...@us...> - 2004-10-06 05:21:11
|
Update of /cvsroot/pywin32/pywin32/dotnet/compiler In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv18710/dotnet/compiler Removed Files: .cvsignore HelloWorld.py cpy.py make.bat makefile readme.txt testall.bat Log Message: Given IronPython etc, I think we can consider this compiler dead (and can get at it from the attic if necessary) --- .cvsignore DELETED --- --- HelloWorld.py DELETED --- --- cpy.py DELETED --- --- testall.bat DELETED --- --- makefile DELETED --- --- make.bat DELETED --- --- readme.txt DELETED --- |
From: Mark H. <mha...@us...> - 2004-10-06 05:21:10
|
Update of /cvsroot/pywin32/pywin32/dotnet/PyRuntime In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv18710/dotnet/PyRuntime Removed Files: PyRuntime.cs makefile Log Message: Given IronPython etc, I think we can consider this compiler dead (and can get at it from the attic if necessary) --- PyRuntime.cs DELETED --- --- makefile DELETED --- |
From: Mark H. <mha...@us...> - 2004-10-06 05:21:09
|
Update of /cvsroot/pywin32/pywin32/dotnet/compiler/builtins In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv18710/dotnet/compiler/builtins Removed Files: builtins.py Log Message: Given IronPython etc, I think we can consider this compiler dead (and can get at it from the attic if necessary) --- builtins.py DELETED --- |
From: Mark H. <mha...@us...> - 2004-10-06 05:21:07
|
Update of /cvsroot/pywin32/pywin32/dotnet In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv18710/dotnet Removed Files: ManagedPython.html clean.bat makefile readme.txt Log Message: Given IronPython etc, I think we can consider this compiler dead (and can get at it from the attic if necessary) --- clean.bat DELETED --- --- makefile DELETED --- --- readme.txt DELETED --- --- ManagedPython.html DELETED --- |
From: Mark H. <mha...@us...> - 2004-10-06 05:19:05
|
Update of /cvsroot/pywin32/pywin32/Pythonwin In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv18353 Modified Files: stddde.cpp Log Message: Give the app 60 seconds to respond to a DDE request before we assume it timed out - useful when talking to DDE database apps, for example. Index: stddde.cpp =================================================================== RCS file: /cvsroot/pywin32/pywin32/Pythonwin/stddde.cpp,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** stddde.cpp 20 Jan 2004 22:28:52 -0000 1.2 --- stddde.cpp 6 Oct 2004 05:18:54 -0000 1.3 *************** *** 10,14 **** // ! #define DDE_TIMEOUT 5000 // 5 seconds // --- 10,14 ---- // ! #define DDE_TIMEOUT 60000 // a minute. // |
From: Mark H. <mha...@us...> - 2004-10-06 05:17:40
|
Update of /cvsroot/pywin32/pywin32/com/win32comext/shell/src In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv18104 Modified Files: PyIShellFolder.cpp Log Message: Make ParseDisplayName emit a sane error message when the result is not a tuple. Index: PyIShellFolder.cpp =================================================================== RCS file: /cvsroot/pywin32/pywin32/com/win32comext/shell/src/PyIShellFolder.cpp,v retrieving revision 1.8 retrieving revision 1.9 diff -C2 -d -r1.8 -r1.9 *** PyIShellFolder.cpp 2 Jul 2004 04:10:53 -0000 1.8 --- PyIShellFolder.cpp 6 Oct 2004 05:17:15 -0000 1.9 *************** *** 438,444 **** PyObject *obppidl; ULONG chEaten, dwAttributes; - if (!PyArg_ParseTuple(result, "lOl" , &chEaten, &obppidl, &dwAttributes)) - return PyCom_SetAndLogCOMErrorFromPyException("ParseDisplayName", IID_IShellFolder); BOOL bPythonIsHappy = TRUE; if (bPythonIsHappy && !PyObject_AsPIDL(obppidl, ppidl)) bPythonIsHappy = FALSE; if (!bPythonIsHappy) hr = PyCom_SetAndLogCOMErrorFromPyException("ParseDisplayName", IID_IShellFolder); --- 438,450 ---- PyObject *obppidl; ULONG chEaten, dwAttributes; BOOL bPythonIsHappy = TRUE; + if (!PyTuple_Check(result)) { + PyErr_Format(PyExc_TypeError, + "ParseDisplayName must return a tuple if (int, pidl, attr) - got '%s'", + result->ob_type->tp_name); + bPythonIsHappy = FALSE; + } + if (bPythonIsHappy && !PyArg_ParseTuple(result, "lOl" , &chEaten, &obppidl, &dwAttributes)) + bPythonIsHappy = FALSE; if (bPythonIsHappy && !PyObject_AsPIDL(obppidl, ppidl)) bPythonIsHappy = FALSE; if (!bPythonIsHappy) hr = PyCom_SetAndLogCOMErrorFromPyException("ParseDisplayName", IID_IShellFolder); |
From: Mark H. <mha...@us...> - 2004-10-06 05:16:30
|
Update of /cvsroot/pywin32/pywin32 In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17863 Modified Files: pywin32_postinstall.py Log Message: Register the ActiveScript engine. Index: pywin32_postinstall.py =================================================================== RCS file: /cvsroot/pywin32/pywin32/pywin32_postinstall.py,v retrieving revision 1.12 retrieving revision 1.13 diff -C2 -d -r1.12 -r1.13 *** pywin32_postinstall.py 24 Sep 2004 07:33:03 -0000 1.12 --- pywin32_postinstall.py 6 Oct 2004 05:16:08 -0000 1.13 *************** *** 10,13 **** --- 10,14 ---- ("win32com.servers.interp", "Interpreter"), ("win32com.servers.dictionary", "DictionaryPolicy"), + ("win32com.axscript.client.pyscript","PyScript"), ] *************** *** 120,123 **** --- 121,126 ---- __import__(module) mod = sys.modules[module] + flags["finalize_register"] = getattr(mod, "DllRegisterServer", None) + flags["finalize_unregister"] = getattr(mod, "DllUnregisterServer", None) klass = getattr(mod, klass_name) func(klass, **flags) |
From: Mark H. <mha...@us...> - 2004-10-06 05:13:24
|
Update of /cvsroot/pywin32/pywin32 In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17342 Modified Files: setup_win32all.py MANIFEST.in Log Message: bump build to 203, integrate isapi with the build process and add lots of missing demos etc from the source distro. Index: MANIFEST.in =================================================================== RCS file: /cvsroot/pywin32/pywin32/MANIFEST.in,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** MANIFEST.in 6 May 2004 03:41:27 -0000 1.3 --- MANIFEST.in 6 Oct 2004 05:13:00 -0000 1.4 *************** *** 12,15 **** --- 12,16 ---- include win32/Demos/*.py include win32/src/*.h + include win32/src/*.manifest include win32/src/PerfMon/perfmondata.def include win32/src/dbi.def *************** *** 41,51 **** --- 42,58 ---- include com/win32comext/axdebug/src/*.h include com/win32comext/adsi/src/*.h + include com/win32comext/adsi/demos/*.py include com/win32comext/axcontrol/src/*.h include com/win32comext/internet/src/*.h include com/win32comext/mapi/src/*.h include com/win32comext/shell/src/*.h + include com/win32comext/mapi/demos/*.py include com/win32comext/shell/demos/servers/*.py include com/win32comext/shell/demos/*.py + include com/win32comext/shell/test/*.py include com/win32comext/taskscheduler/src/*.h + include com/win32comext/taskscheduler/test/*.py + include com/win32comext/ifilter/src/*.h + include com/win32comext/ifilter/demo/*.py # Pythonwin *************** *** 60,63 **** --- 67,75 ---- include pythonwin/res/*.BMP include pythonwin/pywin/*.cfg + include pythonwin/pywin/Demos/*.py + include pythonwin/pywin/Demos/*.txt + include pythonwin/pywin/Demos/app/*.py + include pythonwin/pywin/Demos/app/*.txt + include pythonwin/pywin/Demos/ocx/*.py include pythonwin/License.txt # scintilla *************** *** 75,78 **** --- 87,100 ---- include pythonwin/Scintilla/win32/*.cur + # ISAPI + include isapi/doc/*.html + include isapi/*.txt + include isapi/samples/*.py + include isapi/samples/*.txt + include isapi/test/*.py + include isapi/test/*.txt + + include isapi/src/*.h + # Other tools include SWIG/swig.exe Index: setup_win32all.py =================================================================== RCS file: /cvsroot/pywin32/pywin32/setup_win32all.py,v retrieving revision 1.34 retrieving revision 1.35 diff -C2 -d -r1.34 -r1.35 *** setup_win32all.py 24 Sep 2004 07:26:37 -0000 1.34 --- setup_win32all.py 6 Oct 2004 05:12:53 -0000 1.35 *************** *** 1,3 **** ! build_number=202 # Putting buildno at the top prevents automatic __doc__ assignment, and # I *want* the build number at the top :) --- 1,3 ---- ! build_number=203 # Putting buildno at the top prevents automatic __doc__ assignment, and # I *want* the build number at the top :) *************** *** 109,112 **** --- 109,113 ---- windows_h_version=None, # min version of windows.h needed. extra_swig_commands=None, + is_regular_dll=False, # regular Windows DLL? # list of headers which may not be installed forcing us to # skip this extension *************** *** 136,139 **** --- 137,141 ---- self.windows_h_version = windows_h_version self.optional_headers = optional_headers + self.is_regular_dll = is_regular_dll Extension.__init__ (self, name, sources, include_dirs, *************** *** 200,203 **** --- 202,209 ---- return "win32" + class WinExt_ISAPI(WinExt): + def get_pywin32_dir(self): + return "isapi" + # Note this is used only for "win32com extensions", not pythoncom # itself - thus, output is "win32comext" *************** *** 632,639 **** extra = self.debug and "_d.exe" or ".exe" return r"pythonwin\Pythonwin" + extra return build_ext.get_ext_filename(self, name) def get_export_symbols(self, ext): ! if ext.name.endswith("perfmondata"): return ext.export_symbols return build_ext.get_export_symbols(self, ext) --- 638,648 ---- extra = self.debug and "_d.exe" or ".exe" return r"pythonwin\Pythonwin" + extra + elif name.endswith("isapi.PyISAPI_loader"): + extra = self.debug and "_d.dll" or ".dll" + return r"isapi\PyISAPI_loader" + extra return build_ext.get_ext_filename(self, name) def get_export_symbols(self, ext): ! if ext.is_regular_dll: return ext.export_symbols return build_ext.get_export_symbols(self, ext) *************** *** 716,720 **** # build for any platform sees these as dirty. # This could probably go once we generate .cpp into the temp dir. ! if newer(os.path.abspath(source), os.path.abspath(target)): swig_cmd.extend(["-o", os.path.abspath(target), --- 725,729 ---- # build for any platform sees these as dirty. # This could probably go once we generate .cpp into the temp dir. ! if self.force or newer(os.path.abspath(source), os.path.abspath(target)): swig_cmd.extend(["-o", os.path.abspath(target), *************** *** 798,801 **** --- 807,811 ---- extra_compile_args=["-DUNICODE", "-D_UNICODE", "-DWINNT"], export_symbol_file = "win32/src/PerfMon/perfmondata.def", + is_regular_dll = 1, ), ) *************** *** 863,867 **** libraries = "user32 ole32 advapi32 shell32", dsp_file = r"win32\Pythonservice servicemanager.dsp", ! windows_h_version = 0x500) ] --- 873,877 ---- libraries = "user32 ole32 advapi32 shell32", dsp_file = r"win32\Pythonservice servicemanager.dsp", ! windows_h_version = 0x500), ] *************** *** 915,918 **** --- 925,944 ---- ] + other_extensions = [ + WinExt_ISAPI('PyISAPI_loader', + sources=[os.path.join("isapi", "src", s) for s in + """PyExtensionObjects.cpp PyFilterObjects.cpp + pyISAPI.cpp PythonEng.cpp StdAfx.cpp + Utils.cpp + """.split()], + pch_header = "StdAfx.h", + is_regular_dll = 1, + export_symbols = """HttpExtensionProc GetExtensionVersion + TerminateExtension GetFilterVersion + HttpFilterProc TerminateFilter + PyISAPISetOptions""".split(), + ), + ] + W32_exe_files = [ WinExt_win32("win32popenWin9x", *************** *** 1056,1059 **** --- 1082,1086 ---- 'pythonwin.pywin.scintilla', 'pythonwin.pywin.tools', + 'isapi', ] *************** *** 1090,1098 **** scripts = ["pywin32_postinstall.py"], ! ext_modules = win32_extensions + com_extensions + pythonwin_extensions, package_dir = {"win32com": "com/win32com", "win32comext": "com/win32comext", ! "pythonwin": "pythonwin"}, packages = packages, py_modules = py_modules, --- 1117,1126 ---- scripts = ["pywin32_postinstall.py"], ! ext_modules = win32_extensions + com_extensions + pythonwin_extensions + \ ! other_extensions, package_dir = {"win32com": "com/win32com", "win32comext": "com/win32comext", ! "pythonwin": "pythonwin",}, packages = packages, py_modules = py_modules, *************** *** 1116,1119 **** --- 1144,1148 ---- # win32com docs 'com/win32com/HTML/*', + 'com/win32comext/adsi/demos/*', # Active Scripting test and demos. 'com/win32comext/axscript/test/*', *************** *** 1125,1128 **** --- 1154,1163 ---- 'com/win32comext/taskscheduler/test/*.py', 'com/win32comext/ifilter/demo/*.py', + 'isapi/*.txt', + 'isapi/samples/*.py', + 'isapi/samples/*.txt', + 'isapi/doc/*.html', + 'isapi/test/*.py', + 'isapi/test/*.txt', ]) + # The headers and .lib files |
From: Mark H. <mha...@us...> - 2004-10-06 05:12:37
|
Update of /cvsroot/pywin32/pywin32/pyisapi/src In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17001/pyisapi/src Removed Files: ControlBlock.h FilterContext.h PyExtensionObjects.cpp PyExtensionObjects.h PyFilterObjects.cpp PyFilterObjects.h PythonEng.cpp PythonEng.h StdAfx.cpp StdAfx.h Utils.cpp Utils.h pyISAPI.cpp pyISAPI.h Log Message: Rename pyisapi directory to isapi, so it matches what the user sees, as part of the process of integrating it with the rest of the build. --- ControlBlock.h DELETED --- --- Utils.h DELETED --- --- PyFilterObjects.h DELETED --- --- pyISAPI.cpp DELETED --- --- PythonEng.cpp DELETED --- --- PyExtensionObjects.h DELETED --- --- PyFilterObjects.cpp DELETED --- --- pyISAPI.h DELETED --- --- Utils.cpp DELETED --- --- PyExtensionObjects.cpp DELETED --- --- FilterContext.h DELETED --- --- StdAfx.h DELETED --- --- StdAfx.cpp DELETED --- --- PythonEng.h DELETED --- |
From: Mark H. <mha...@us...> - 2004-10-06 05:12:11
|
Update of /cvsroot/pywin32/pywin32/pyisapi/isapi In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17001/pyisapi/isapi Removed Files: __init__.py install.py isapicon.py simple.py threaded_extension.py Log Message: Rename pyisapi directory to isapi, so it matches what the user sees, as part of the process of integrating it with the rest of the build. --- isapicon.py DELETED --- --- threaded_extension.py DELETED --- --- simple.py DELETED --- --- __init__.py DELETED --- --- install.py DELETED --- |
From: Mark H. <mha...@us...> - 2004-10-06 05:12:11
|
Update of /cvsroot/pywin32/pywin32/pyisapi/doc In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17001/pyisapi/doc Removed Files: isapi.html Log Message: Rename pyisapi directory to isapi, so it matches what the user sees, as part of the process of integrating it with the rest of the build. --- isapi.html DELETED --- |
From: Mark H. <mha...@us...> - 2004-10-06 05:12:09
|
Update of /cvsroot/pywin32/pywin32/pyisapi/samples In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17001/pyisapi/samples Removed Files: .cvsignore README.txt advanced.py redirector.py Log Message: Rename pyisapi directory to isapi, so it matches what the user sees, as part of the process of integrating it with the rest of the build. --- .cvsignore DELETED --- --- README.txt DELETED --- --- advanced.py DELETED --- --- redirector.py DELETED --- |
From: Mark H. <mha...@us...> - 2004-10-06 05:12:09
|
Update of /cvsroot/pywin32/pywin32/pyisapi In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17001/pyisapi Removed Files: README.txt setup.py Log Message: Rename pyisapi directory to isapi, so it matches what the user sees, as part of the process of integrating it with the rest of the build. --- setup.py DELETED --- --- README.txt DELETED --- |
From: Mark H. <mha...@us...> - 2004-10-06 05:12:06
|
Update of /cvsroot/pywin32/pywin32/isapi/test In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17001/isapi/test Added Files: README.txt extension_simple.py Log Message: Rename pyisapi directory to isapi, so it matches what the user sees, as part of the process of integrating it with the rest of the build. --- NEW FILE: extension_simple.py --- # This is an ISAPI extension purely for testing purposes. It is NOT # a 'demo' (even though it may be useful!) # # Install this extension, then point your browser to: # "http://localhost/pyisapi_test/test1" # This will execute the method 'test1' below. You can specify any method name # at all, but currently there is only 1. from isapi import isapicon, threaded_extension, ExtensionError from isapi.simple import SimpleFilter import traceback import urllib import winerror # If we have no console (eg, am running from inside IIS), redirect output # somewhere useful - in this case, the standard win32 trace collector. import win32api try: win32api.GetConsoleTitle() except win32api.error: # No console - redirect import win32traceutil # The ISAPI extension - handles requests in our virtual dir, and sends the # response to the client. class Extension(threaded_extension.ThreadPoolExtension): "Python ISAPI Tester" def Dispatch(self, ecb): print 'Tester dispatching "%s"' % (ecb.GetServerVariable("URL"),) url = ecb.GetServerVariable("URL") test_name = url.split("/")[-1] meth = getattr(self, test_name, None) if meth is None: raise AttributeError, "No test named '%s'" % (test_name,) result = meth(ecb) ecb.SendResponseHeaders("200 OK", "Content-type: text/html\r\n\r\n", False) print >> ecb, "<HTML><BODY>OK" if result: print >> ecb, "<pre>" print >> ecb, result print >> ecb, "</pre>" print >> ecb, "</BODY></HTML>" ecb.DoneWithSession() def test1(self, ecb): try: ecb.GetServerVariable("foo bar") raise RuntimeError, "should have failed!" except ExtensionError, err: assert err.errno == winerror.ERROR_INVALID_INDEX, err return "worked!" # The entry points for the ISAPI extension. def __ExtensionFactory__(): return Extension() if __name__=='__main__': # If run from the command-line, install ourselves. from isapi.install import * params = ISAPIParameters() # Setup the virtual directories - this is a list of directories our # extension uses - in this case only 1. # Each extension has a "script map" - this is the mapping of ISAPI # extensions. sm = [ ScriptMapParams(Extension="*", Flags=0) ] vd = VirtualDirParameters(Name="pyisapi_test", Description = Extension.__doc__, ScriptMaps = sm, ScriptMapUpdate = "replace" ) params.VirtualDirs = [vd] HandleCommandLine(params) --- NEW FILE: README.txt --- This is a directory for tests of the PyISAPI framework. For demos, please see the pyisapi 'samples' directory. |
From: Mark H. <mha...@us...> - 2004-10-06 05:12:06
|
Update of /cvsroot/pywin32/pywin32/isapi In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17001/isapi Added Files: README.txt __init__.py install.py isapicon.py simple.py threaded_extension.py Log Message: Rename pyisapi directory to isapi, so it matches what the user sees, as part of the process of integrating it with the rest of the build. --- NEW FILE: isapicon.py --- """Constants needed by ISAPI filters and extensions.""" # ====================================================================== # Copyright 2002-2003 by Blackdog Software Pty Ltd. # # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee is hereby # granted, provided that the above copyright notice appear in all # copies and that both that copyright notice and this permission # notice appear in supporting documentation, and that the name of # Blackdog Software not be used in advertising or publicity pertaining to # distribution of the software without specific, written prior # permission. # # BLACKDOG SOFTWARE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, # INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN # NO EVENT SHALL BLACKDOG SOFTWARE BE LIABLE FOR ANY SPECIAL, INDIRECT OR # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS # OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # ====================================================================== # HTTP reply codes HTTP_CONTINUE = 100 HTTP_SWITCHING_PROTOCOLS = 101 HTTP_PROCESSING = 102 HTTP_OK = 200 HTTP_CREATED = 201 HTTP_ACCEPTED = 202 HTTP_NON_AUTHORITATIVE = 203 HTTP_NO_CONTENT = 204 HTTP_RESET_CONTENT = 205 HTTP_PARTIAL_CONTENT = 206 HTTP_MULTI_STATUS = 207 HTTP_MULTIPLE_CHOICES = 300 HTTP_MOVED_PERMANENTLY = 301 HTTP_MOVED_TEMPORARILY = 302 HTTP_SEE_OTHER = 303 HTTP_NOT_MODIFIED = 304 HTTP_USE_PROXY = 305 HTTP_TEMPORARY_REDIRECT = 307 HTTP_BAD_REQUEST = 400 HTTP_UNAUTHORIZED = 401 HTTP_PAYMENT_REQUIRED = 402 HTTP_FORBIDDEN = 403 HTTP_NOT_FOUND = 404 HTTP_METHOD_NOT_ALLOWED = 405 HTTP_NOT_ACCEPTABLE = 406 HTTP_PROXY_AUTHENTICATION_REQUIRED= 407 HTTP_REQUEST_TIME_OUT = 408 HTTP_CONFLICT = 409 HTTP_GONE = 410 HTTP_LENGTH_REQUIRED = 411 HTTP_PRECONDITION_FAILED = 412 HTTP_REQUEST_ENTITY_TOO_LARGE = 413 HTTP_REQUEST_URI_TOO_LARGE = 414 HTTP_UNSUPPORTED_MEDIA_TYPE = 415 HTTP_RANGE_NOT_SATISFIABLE = 416 HTTP_EXPECTATION_FAILED = 417 HTTP_UNPROCESSABLE_ENTITY = 422 HTTP_INTERNAL_SERVER_ERROR = 500 HTTP_NOT_IMPLEMENTED = 501 HTTP_BAD_GATEWAY = 502 HTTP_SERVICE_UNAVAILABLE = 503 HTTP_GATEWAY_TIME_OUT = 504 HTTP_VERSION_NOT_SUPPORTED = 505 HTTP_VARIANT_ALSO_VARIES = 506 HSE_STATUS_SUCCESS = 1 HSE_STATUS_SUCCESS_AND_KEEP_CONN = 2 HSE_STATUS_PENDING = 3 HSE_STATUS_ERROR = 4 SF_NOTIFY_SECURE_PORT = 0x00000001 SF_NOTIFY_NONSECURE_PORT = 0x00000002 SF_NOTIFY_READ_RAW_DATA = 0x00008000 SF_NOTIFY_PREPROC_HEADERS = 0x00004000 SF_NOTIFY_AUTHENTICATION = 0x00002000 SF_NOTIFY_URL_MAP = 0x00001000 SF_NOTIFY_ACCESS_DENIED = 0x00000800 SF_NOTIFY_SEND_RESPONSE = 0x00000040 SF_NOTIFY_SEND_RAW_DATA = 0x00000400 SF_NOTIFY_LOG = 0x00000200 SF_NOTIFY_END_OF_REQUEST = 0x00000080 SF_NOTIFY_END_OF_NET_SESSION = 0x00000100 SF_NOTIFY_ORDER_HIGH = 0x00080000 SF_NOTIFY_ORDER_MEDIUM = 0x00040000 SF_NOTIFY_ORDER_LOW = 0x00020000 SF_NOTIFY_ORDER_DEFAULT = SF_NOTIFY_ORDER_LOW SF_NOTIFY_ORDER_MASK = (SF_NOTIFY_ORDER_HIGH | \ SF_NOTIFY_ORDER_MEDIUM | \ SF_NOTIFY_ORDER_LOW) SF_STATUS_REQ_FINISHED = 134217728 # 0x8000000 SF_STATUS_REQ_FINISHED_KEEP_CONN = 134217728 + 1 SF_STATUS_REQ_NEXT_NOTIFICATION = 134217728 + 2 SF_STATUS_REQ_HANDLED_NOTIFICATION = 134217728 + 3 SF_STATUS_REQ_ERROR = 134217728 + 4 SF_STATUS_REQ_READ_NEXT = 134217728 + 5 --- NEW FILE: threaded_extension.py --- """An ISAPI extension base class implemented using a thread-pool.""" # $Id: threaded_extension.py,v 1.1 2004/10/06 05:11:52 mhammond Exp $ import sys from isapi import isapicon import isapi.simple from win32file import GetQueuedCompletionStatus, CreateIoCompletionPort, \ PostQueuedCompletionStatus, CloseHandle from win32security import SetThreadToken from win32event import INFINITE from pywintypes import OVERLAPPED # Python 2.3 and earlier insists on "C" locale - if it isn't, subtle things # break, such as floating point constants loaded from .pyc files. # The threading module uses such floating-points as an argument to sleep(), # resulting in extremely long sleeps when tiny intervals are specified. # We can work around this by resetting the C locale before the import. if sys.hexversion < 0x02040000: import locale locale.setlocale(locale.LC_NUMERIC, "C") import threading import traceback ISAPI_REQUEST = 1 ISAPI_SHUTDOWN = 2 class WorkerThread(threading.Thread): def __init__(self, extension, io_req_port): self.running = False self.io_req_port = io_req_port self.extension = extension threading.Thread.__init__(self) def run(self): self.running = True while self.running: errCode, bytes, key, overlapped = \ GetQueuedCompletionStatus(self.io_req_port, INFINITE) if key == ISAPI_SHUTDOWN and overlapped is None: break # Let the parent extension handle the command. dispatcher = self.extension.dispatch_map.get(key) if dispatcher is None: raise RuntimeError, "Bad request '%s'" % (key,) dispatcher(errCode, bytes, key, overlapped) def call_handler(self, cblock): self.extension.Dispatch(cblock) # A generic thread-pool based extension, using IO Completion Ports. # Sub-classes can override one method to implement a simple extension, or # may leverage the CompletionPort to queue their own requests, and implement a # fully asynch extension. class ThreadPoolExtension(isapi.simple.SimpleExtension): "Base class for an ISAPI extension based around a thread-pool" max_workers = 20 worker_shutdown_wait = 15000 # 15 seconds for workers to quit. XXX - per thread!!! Fix me! def __init__(self): self.workers = [] # extensible dispatch map, for sub-classes that need to post their # own requests to the completion port. # Each of these functions is called with the result of # GetQueuedCompletionStatus for our port. self.dispatch_map = { ISAPI_REQUEST: self.DispatchConnection, } def GetExtensionVersion(self, vi): isapi.simple.SimpleExtension.GetExtensionVersion(self, vi) # As per Q192800, the CompletionPort should be created with the number # of processors, even if the number of worker threads is much larger. # Passing 0 means the system picks the number. self.io_req_port = CreateIoCompletionPort(-1, None, 0, 0) # start up the workers self.workers = [] for i in range(self.max_workers): worker = WorkerThread(self, self.io_req_port) worker.start() self.workers.append(worker) def HttpExtensionProc(self, control_block): overlapped = OVERLAPPED() overlapped.object = control_block PostQueuedCompletionStatus(self.io_req_port, 0, ISAPI_REQUEST, overlapped) return isapicon.HSE_STATUS_PENDING def TerminateExtension(self, status): for worker in self.workers: worker.running = False for worker in self.workers: PostQueuedCompletionStatus(self.io_req_port, 0, ISAPI_SHUTDOWN, None) for worker in self.workers: worker.join(self.worker_shutdown_wait) self.dispatch_map = {} # break circles CloseHandle(self.io_req_port) # This is the one operation the base class supports - a simple # Connection request. We setup the thread-token, and dispatch to the # sub-class's 'Dispatch' method. def DispatchConnection(self, errCode, bytes, key, overlapped): control_block = overlapped.object # setup the correct user for this request hRequestToken = control_block.GetImpersonationToken() SetThreadToken(None, hRequestToken) try: try: self.Dispatch(control_block) except: self.HandleDispatchError(control_block) finally: # reset the security context SetThreadToken(None, None) def Dispatch(self, ecb): """Overridden by the sub-class to handle connection requests. This class creates a thread-pool using a Windows completion port, and dispatches requests via this port. Sub-classes can generally implementeach connection request using blocking reads and writes, and the thread-pool will still provide decent response to the end user. The sub-class can set a max_workers attribute (default is 20). Note that this generally does *not* mean 20 threads will all be concurrently running, via the magic of Windows completion ports. There is no default implementation - sub-classes must implement this. """ raise NotImplementedError, "sub-classes should override Dispatch" def HandleDispatchError(self, ecb): """Handles errors in the Dispatch method. When a Dispatch method call fails, this method is called to handle the exception. The default implementation formats the traceback in the browser. """ ecb.HttpStatusCode = isapicon.HSE_STATUS_ERROR #control_block.LogData = "we failed!" ecb.SendResponseHeaders("200 OK", "Content-type: text/html\r\n\r\n", False) exc_typ, exc_val, exc_tb = sys.exc_info() limit = None try: try: import cgi print >> ecb print >> ecb, "<H3>Traceback (most recent call last):</H3>" list = traceback.format_tb(exc_tb, limit) + \ traceback.format_exception_only(exc_typ, exc_val) print >> ecb, "<PRE>%s<B>%s</B></PRE>" % ( cgi.escape("".join(list[:-1])), cgi.escape(list[-1]),) except: print "FAILED to render the error message!" traceback.print_exc() print "ORIGINAL extension error:" traceback.print_exception(exc_typ, exc_val, exc_tb) finally: # holding tracebacks in a local of a frame that may itself be # part of a traceback used to be evil and cause leaks! exc_tb = None ecb.DoneWithSession() --- NEW FILE: simple.py --- """Simple base-classes for extensions and filters. None of the filter and extension functions are considered 'optional' by the framework. These base-classes provide simple implementations for the Initialize and Terminate functions, allowing you to omit them, It is not necessary to use these base-classes - but if you don't, you must ensure each of the required methods are implemented. """ class SimpleExtension: "Base class for a a simple ISAPI extension" def __init__(self): pass def GetExtensionVersion(self, vi): """Called by the ISAPI framework to get the extension version The default implementation uses the classes docstring to set the extension description.""" # nod to our reload capability - vi is None when we are reloaded. if vi is not None: vi.ExtensionDesc = self.__doc__ def HttpExtensionProc(self, control_block): """Called by the ISAPI framework for each extension request. sub-classes must provide an implementation for this method. """ raise NotImplementedError, "sub-classes should override HttpExtensionProc" def TerminateExtension(self, status): """Called by the ISAPI framework as the extension terminates. """ pass class SimpleFilter: "Base class for a a simple ISAPI filter" filter_flags = None def __init__(self): pass def GetFilterVersion(self, fv): """Called by the ISAPI framework to get the extension version The default implementation uses the classes docstring to set the extension description, and uses the classes filter_flags attribute to set the ISAPI filter flags - you must specify filter_flags in your class. """ if self.filter_flags is None: raise RuntimeError, "You must specify the filter flags" # nod to our reload capability - fv is None when we are reloaded. if fv is not None: fv.Flags = self.filter_flags fv.FilterDesc = self.__doc__ def HttpFilterProc(self, fc): """Called by the ISAPI framework for each filter request. sub-classes must provide an implementation for this method. """ raise NotImplementedError, "sub-classes should override HttpExtensionProc" def TerminateFilter(self, status): """Called by the ISAPI framework as the filter terminates. """ pass --- NEW FILE: __init__.py --- # The Python ISAPI package. # Exceptions thrown by the DLL framework. class ISAPIError(Exception): def __init__(self, errno, strerror = None, funcname = None): # named attributes match IOError etc. self.errno = errno self.strerror = strerror self.funcname = funcname Exception.__init__(self, errno, strerror, funcname) def __str__(self): if self.strerror is None: try: import win32api self.strerror = win32api.FormatMessage(self.errno).strip() except: self.strerror = "no error message is available" # str() looks like a win32api error. return str( (self.errno, self.strerror, self.funcname) ) class FilterError(ISAPIError): pass class ExtensionError(ISAPIError): pass # A little development aid - a filter or extension callback function can # raise one of these exceptions, and the handler module will be reloaded. # This means you can change your code without restarting IIS. # After a reload, your filter/extension will have the GetFilterVersion/ # GetExtensionVersion function called, but with None as the first arg. class InternalReloadException(Exception): pass --- NEW FILE: install.py --- """Installation utilities for Python ISAPI filters and extensions.""" # this code adapted from "Tomcat JK2 ISAPI redirector", part of Apache # Created July 2004, Mark Hammond. import sys, os, imp, shutil, stat from win32com.client import GetObject, Dispatch from win32com.client.gencache import EnsureModule, EnsureDispatch import pythoncom import winerror import traceback _APP_INPROC = 0 _APP_OUTPROC = 1 _APP_POOLED = 2 _IIS_OBJECT = "IIS://LocalHost/W3SVC" _IIS_SERVER = "IIsWebServer" _IIS_WEBDIR = "IIsWebDirectory" _IIS_WEBVIRTUALDIR = "IIsWebVirtualDir" _IIS_FILTERS = "IIsFilters" _IIS_FILTER = "IIsFilter" _DEFAULT_SERVER_NAME = "Default Web Site" _DEFAULT_HEADERS = "X-Powered-By: Python" _DEFAULT_PROTECTION = _APP_POOLED # Default is for 'execute' only access - ie, only the extension # can be used. This can be overridden via your install script. _DEFAULT_ACCESS_EXECUTE = True _DEFAULT_ACCESS_READ = False _DEFAULT_ACCESS_WRITE = False _DEFAULT_ACCESS_SCRIPT = False _DEFAULT_CONTENT_INDEXED = False _DEFAULT_ENABLE_DIR_BROWSING = False _DEFAULT_ENABLE_DEFAULT_DOC = False is_debug_build = False for imp_ext, _, _ in imp.get_suffixes(): if imp_ext == "_d.pyd": is_debug_build = True break this_dir = os.path.abspath(os.path.dirname(__file__)) class FilterParameters: Name = None Description = None Path = None Server = None def __init__(self, **kw): self.__dict__.update(kw) class VirtualDirParameters: Name = None # Must be provided. Description = None # defaults to Name AppProtection = _DEFAULT_PROTECTION Headers = _DEFAULT_HEADERS Path = None # defaults to WWW root. AccessExecute = _DEFAULT_ACCESS_EXECUTE AccessRead = _DEFAULT_ACCESS_READ AccessWrite = _DEFAULT_ACCESS_WRITE AccessScript = _DEFAULT_ACCESS_SCRIPT ContentIndexed = _DEFAULT_CONTENT_INDEXED EnableDirBrowsing = _DEFAULT_ENABLE_DIR_BROWSING EnableDefaultDoc = _DEFAULT_ENABLE_DEFAULT_DOC ScriptMaps = [] ScriptMapUpdate = "end" # can be 'start', 'end', 'replace' Server = None def __init__(self, **kw): self.__dict__.update(kw) class ScriptMapParams: Extension = None Module = None Flags = 5 Verbs = "" def __init__(self, **kw): self.__dict__.update(kw) class ISAPIParameters: ServerName = _DEFAULT_SERVER_NAME # Description = None Filters = [] VirtualDirs = [] def __init__(self, **kw): self.__dict__.update(kw) verbose = 1 # The level - 0 is quiet. def log(level, what): if verbose >= level: print what # Convert an ADSI COM exception to the Win32 error code embedded in it. def _GetWin32ErrorCode(com_exc): hr, msg, exc, narg = com_exc # If we have more details in the 'exc' struct, use it. if exc: hr = exc[-1] if winerror.HRESULT_FACILITY(hr) != winerror.FACILITY_WIN32: raise return winerror.SCODE_CODE(hr) class InstallationError(Exception): pass class ItemNotFound(InstallationError): pass class ConfigurationError(InstallationError): pass def FindPath(options, server, name): if name.lower().startswith("iis://"): return name else: if name and name[0] != "/": name = "/"+name return FindWebServer(options, server)+"/ROOT"+name def FindWebServer(options, server_desc): # command-line options get first go. if options.server: server = options.server # If the config passed by the caller doesn't specify one, use the default elif server_desc is None: server = _IIS_OBJECT+"/1" else: server = server_desc # Check it is good. try: GetObject(server) except pythoncom.com_error, details: hr, msg, exc, arg_err = details if exc and exc[2]: msg = exc[2] raise ItemNotFound, \ "WebServer %s: %s" % (server, msg) return server def CreateDirectory(params, options): if not params.Name: raise ConfigurationError, "No Name param" slash = params.Name.rfind("/") if slash >= 0: parent = params.Name[:slash] name = params.Name[slash+1:] else: parent = "" name = params.Name webDir = GetObject(FindPath(options, params.Server, parent)) if parent: # Note that the directory won't be visible in the IIS UI # unless the directory exists on the filesystem. keyType = _IIS_WEBDIR else: keyType = _IIS_WEBVIRTUALDIR _CallHook(params, "PreInstall", options) try: newDir = webDir.Create(keyType, name) except pythoncom.com_error, details: rc = _GetWin32ErrorCode(details) if rc != winerror.ERROR_ALREADY_EXISTS: raise newDir = GetObject(FindPath(options, params.Server, params.Name)) log(2, "Updating existing directory '%s'..." % (params.Name,)) else: log(2, "Creating new directory '%s'..." % (params.Name,)) friendly = params.Description or params.Name newDir.AppFriendlyName = friendly try: path = params.Path or webDir.Path newDir.Path = path except AttributeError: pass newDir.AppCreate2(params.AppProtection) newDir.HttpCustomHeaders = params.Headers log(2, "Setting directory options...") newDir.AccessExecute = params.AccessExecute newDir.AccessRead = params.AccessRead newDir.AccessWrite = params.AccessWrite newDir.AccessScript = params.AccessScript newDir.ContentIndexed = params.ContentIndexed newDir.EnableDirBrowsing = params.EnableDirBrowsing newDir.EnableDefaultDoc = params.EnableDefaultDoc newDir.SetInfo() smp_items = [] for smp in params.ScriptMaps: item = "%s,%s,%s" % (smp.Extension, smp.Module, smp.Flags) # IIS gets upset if there is a trailing verb comma, but no verbs if smp.Verbs: item += "," + smp.Verbs smp_items.append(item) if params.ScriptMapUpdate == "replace": newDir.ScriptMaps = smp_items elif params.ScriptMapUpdate == "end": for item in smp_items: if item not in newDir.ScriptMaps: newDir.ScriptMaps = newDir.ScriptMaps + (item,) elif params.ScriptMapUpdate == "start": for item in smp_items: if item not in newDir.ScriptMaps: newDir.ScriptMaps = (item,) + newDir.ScriptMaps else: raise ConfigurationError, \ "Unknown ScriptMapUpdate option '%s'" % (params.ScriptMapUpdate,) newDir.SetInfo() _CallHook(params, "PostInstall", options, newDir) log(1, "Configured Virtual Directory: %s" % (params.Name,)) return newDir def CreateISAPIFilter(filterParams, options): server = FindWebServer(options, filterParams.Server) _CallHook(filterParams, "PreInstall", options) try: filters = GetObject(server+"/Filters") except pythoncom.com_error, (hr, msg, exc, arg): # Brand new sites don't have the '/Filters' collection - create it. # Any errors other than 'not found' we shouldn't ignore. if winerror.HRESULT_FACILITY(hr) != winerror.FACILITY_WIN32 or \ winerror.HRESULT_CODE(hr) != winerror.ERROR_PATH_NOT_FOUND: raise server_ob = GetObject(server) filters = server_ob.Create(_IIS_FILTERS, "Filters") filters.FilterLoadOrder = "" filters.SetInfo() try: newFilter = filters.Create(_IIS_FILTER, filterParams.Name) log(2, "Created new ISAPI filter...") except pythoncom.com_error, (hr, msg, exc, narg): if exc is None or exc[-1]!=-2147024713: raise log(2, "Updating existing filter '%s'..." % (filterParams.Name,)) newFilter = GetObject(server+"/Filters/"+filterParams.Name) assert os.path.isfile(filterParams.Path) newFilter.FilterPath = filterParams.Path newFilter.FilterDescription = filterParams.Description newFilter.SetInfo() load_order = [b.strip() for b in filters.FilterLoadOrder.split(",")] if filterParams.Name not in load_order: load_order.append(filterParams.Name) filters.FilterLoadOrder = ",".join(load_order) filters.SetInfo() _CallHook(filterParams, "PostInstall", options, newFilter) log (1, "Configured Filter: %s" % (filterParams.Name,)) return newFilter def DeleteISAPIFilter(filterParams, options): _CallHook(filterParams, "PreRemove", options) server = FindWebServer(options, filterParams.Server) filters = GetObject(server+"/Filters") try: filters.Delete(_IIS_FILTER, filterParams.Name) log(2, "Deleted ISAPI filter '%s'" % (filterParams.Name,)) except pythoncom.com_error, details: rc = _GetWin32ErrorCode(details) if rc != winerror.ERROR_PATH_NOT_FOUND: raise log(2, "ISAPI filter '%s' did not exist." % (filterParams.Name,)) if filterParams.Path: load_order = [b.strip() for b in filters.FilterLoadOrder.split(",")] if filterParams.Path in load_order: load_order.remove(filterParams.Path) filters.FilterLoadOrder = ",".join(load_order) filters.SetInfo() _CallHook(filterParams, "PostRemove", options) log (1, "Deleted Filter: %s" % (filterParams.Name,)) def CheckLoaderModule(dll_name): suffix = "" if is_debug_build: suffix = "_d" template = os.path.join(this_dir, "PyISAPI_loader" + suffix + ".dll") if not os.path.isfile(template): raise ConfigurationError, \ "Template loader '%s' does not exist" % (template,) # We can't do a simple "is newer" check, as the DLL is specific to the # Python version. So we check the date-time and size are identical, # and skip the copy in that case. src_stat = os.stat(template) try: dest_stat = os.stat(dll_name) except os.error: same = 0 else: same = src_stat[stat.ST_SIZE]==dest_stat[stat.ST_SIZE] and \ src_stat[stat.ST_MTIME]==dest_stat[stat.ST_MTIME] if not same: log(2, "Updating %s->%s" % (template, dll_name)) shutil.copyfile(template, dll_name) shutil.copystat(template, dll_name) else: log(2, "%s is up to date." % (dll_name,)) def _CallHook(ob, hook_name, options, *extra_args): func = getattr(ob, hook_name, None) if func is not None: args = (ob,options) + extra_args func(*args) def Install(params, options): _CallHook(params, "PreInstall", options) for vd in params.VirtualDirs: CreateDirectory(vd, options) for filter_def in params.Filters: CreateISAPIFilter(filter_def, options) _CallHook(params, "PostInstall", options) def Uninstall(params, options): _CallHook(params, "PreRemove", options) for vd in params.VirtualDirs: _CallHook(vd, "PreRemove", options) try: directory = GetObject(FindPath(options, vd.Server, vd.Name)) directory.AppUnload() parent = GetObject(directory.Parent) parent.Delete(directory.Class, directory.Name) except pythoncom.com_error, details: rc = _GetWin32ErrorCode(details) if rc != winerror.ERROR_PATH_NOT_FOUND: raise _CallHook(vd, "PostRemove", options) log (1, "Deleted Virtual Directory: %s" % (vd.Name,)) for filter_def in params.Filters: DeleteISAPIFilter(filter_def, options) _CallHook(params, "PostRemove", options) # Patch up any missing module names in the params, replacing them with # the DLL name that hosts this extension/filter. def _PatchParamsModule(params, dll_name, file_must_exist = True): if file_must_exist: if not os.path.isfile(dll_name): raise ConfigurationError, "%s does not exist" % (dll_name,) # Patch up all references to the DLL. for f in params.Filters: if f.Path is None: f.Path = dll_name for d in params.VirtualDirs: for sm in d.ScriptMaps: if sm.Module is None: sm.Module = dll_name def GetLoaderModuleName(mod_name): # find the name of the DLL hosting us. # By default, this is "_{module_base_name}.dll" if hasattr(sys, "frozen"): # What to do? The .dll knows its name, but this is likely to be # executed via a .exe, which does not know. base, ext = os.path.splitext(mod_name) path, base = os.path.split(base) # handle the common case of 'foo.exe'/'foow.exe' if base.endswith('w'): base = base[:-1] # For py2exe, we have '_foo.dll' as the standard pyisapi loader - but # 'foo.dll' is what we use (it just delegates). # So no leading '_' on the installed name. dll_name = os.path.abspath(os.path.join(path, base + ".dll")) else: base, ext = os.path.splitext(mod_name) path, base = os.path.split(base) dll_name = os.path.abspath(os.path.join(path, "_" + base + ".dll")) # Check we actually have it. if not hasattr(sys, "frozen"): CheckLoaderModule(dll_name) return dll_name def InstallModule(conf_module_name, params, options): if not hasattr(sys, "frozen"): conf_module_name = os.path.abspath(conf_module_name) if not os.path.isfile(conf_module_name): raise ConfigurationError, "%s does not exist" % (conf_module_name,) loader_dll = GetLoaderModuleName(conf_module_name) _PatchParamsModule(params, loader_dll) Install(params, options) def UninstallModule(conf_module_name, params, options): loader_dll = GetLoaderModuleName(conf_module_name) _PatchParamsModule(params, loader_dll, False) Uninstall(params, options) standard_arguments = { "install" : "Install the extension", "remove" : "Remove the extension" } # We support 2 ways of extending our command-line/install support. # * Many of the installation items allow you to specify "PreInstall", # "PostInstall", "PreRemove" and "PostRemove" hooks # All hooks are called with the 'params' object being operated on, and # the 'optparser' options for this session (ie, the command-line options) # PostInstall for VirtualDirectories and Filters both have an additional # param - the ADSI object just created. # * You can pass your own option parser for us to use, and/or define a map # with your own custom arg handlers. It is a map of 'arg'->function. # The function is called with (options, log_fn, arg). The function's # docstring is used in the usage output. def HandleCommandLine(params, argv=None, conf_module_name = None, default_arg = "install", opt_parser = None, custom_arg_handlers = {}): """Perform installation or removal of an ISAPI filter or extension. This module handles standard command-line options and configuration information, and installs, removes or updates the configuration of an ISAPI filter or extension. You must pass your configuration information in params - all other arguments are optional, and allow you to configure the installation process. """ global verbose from optparse import OptionParser argv = argv or sys.argv conf_module_name = conf_module_name or sys.argv[0] if opt_parser is None: # Build our own parser. parser = OptionParser(usage='') else: # The caller is providing their own filter, presumably with their # own options all setup. parser = opt_parser # build a usage string if we don't have one. if not parser.get_usage(): all_args = standard_arguments.copy() for arg, handler in custom_arg_handlers.items(): all_args[arg] = handler.__doc__ arg_names = "|".join(all_args.keys()) usage_string = "%prog [options] [" + arg_names + "]\n" usage_string += "commands:\n" for arg, desc in all_args.items(): usage_string += " %-10s: %s" % (arg, desc) + "\n" parser.set_usage(usage_string[:-1]) parser.add_option("-q", "--quiet", action="store_false", dest="verbose", default=True, help="don't print status messages to stdout") parser.add_option("-v", "--verbosity", action="count", dest="verbose", default=1, help="increase the verbosity of status messages") parser.add_option("", "--server", action="store", help="Specifies the IIS server to install/uninstall on." \ " Default is '%s/1'" % (_IIS_OBJECT,)) (options, args) = parser.parse_args(argv[1:]) verbose = options.verbose if not args: args = [default_arg] try: for arg in args: if arg == "install": InstallModule(conf_module_name, params, options) log(1, "Installation complete.") elif arg in ["remove", "uninstall"]: UninstallModule(conf_module_name, params, options) log(1, "Uninstallation complete.") else: handler = custom_arg_handlers.get(arg, None) if handler is None: parser.error("Invalid arg '%s'" % (arg,)) handler(options, log, arg) except (ItemNotFound, InstallationError), details: if options.verbose > 1: traceback.print_exc() print "%s: %s" % (details.__class__.__name__, details) --- NEW FILE: README.txt --- A Python ISAPI extension. Contributed by Phillip Frantz, and is Copyright 2002-2003 by Blackdog Software Pty Ltd. See the 'samples' directory, and particularly samples\README.txt You can find documentation in the PyWin32.chm file that comes with pywin32 - you can open this from Pythonwin->Help, or from the start menu. |
From: Mark H. <mha...@us...> - 2004-10-06 05:12:04
|
Update of /cvsroot/pywin32/pywin32/isapi/samples In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17001/isapi/samples Added Files: .cvsignore README.txt advanced.py redirector.py Log Message: Rename pyisapi directory to isapi, so it matches what the user sees, as part of the process of integrating it with the rest of the build. --- NEW FILE: .cvsignore --- *.dll --- NEW FILE: README.txt --- In this directory you will find examples of ISAPI filters and extensions. The filter loading mechanism works like this: * IIS loads the special Python "loader" DLL. This DLL will generally have a leading underscore as part of its name. * This loader DLL looks for a Python module, by removing the first letter of the DLL base name. This means that an ISAPI extension module consists of 2 key files - the loader DLL (eg, "_MyIISModule.dll", and a Python module (which for this example would be "MyIISModule.py") When you install an ISAPI extension, the installation code checks to see if there is a loader DLL for your implementation file - if one does not exist, or the standard loader is different, it is copied and renamed accordingly. We use this mechanism to provide the maximum separation between different Python extensions installed on the same server - otherwise filter order and other tricky IIS semantics would need to be replicated. Also, each filter gets its own thread-pool, etc. --- NEW FILE: advanced.py --- # This extension demonstrates some advanced features of the Python ISAPI # framework. # We demonstrate: # * Reloading your Python module without shutting down IIS (eg, when your # .py implementation file changes.) # * Custom command-line handling - both additional options and commands. from isapi import isapicon from isapi.simple import SimpleExtension import sys, os, stat if hasattr(sys, "isapidllhandle"): import win32traceutil # Notes on reloading # If your HttpFilterProc or HttpExtensionProc functions raises # 'isapi.InternalReloadException', the framework will not treat it # as an error but instead will terminate your extension, reload your # extension module, re-initialize the instance, and re-issue the request. # The Initialize functions are called with None as their param. The # return code from the terminate function is ignored. # # This is all the framework does to help you. It is up to your code # when you raise this exception. This sample uses a Win32 "find # notification". Whenever windows tells us one of the files in the # directory has changed, we check if the time of our source-file has # changed, and set a flag. Next imcoming request, we check the flag and # raise the special exception if set. # # The end result is that the module is automatically reloaded whenever # the source-file changes - you need take no further action to see your # changes reflected in the running server. # The framework only reloads your module - if you have libraries you # depend on and also want reloaded, you must arrange for this yourself. # One way of doing this would be to special case the import of these # modules. Eg: # -- # try: # my_module = reload(my_module) # module already imported - reload it # except NameError: # import my_module # first time around - import it. # -- # When your module is imported for the first time, the NameError will # be raised, and the module imported. When the ISAPI framework reloads # your module, the existing module will avoid the NameError, and allow # you to reload that module. from isapi import InternalReloadException import win32event, win32file, winerror, win32con, threading try: reload_counter += 1 except NameError: reload_counter = 0 # A watcher thread that checks for __file__ changing. # When it detects it, it simply sets "change_detected" to true. class ReloadWatcherThread(threading.Thread): def __init__(self): self.change_detected = False self.filename = __file__ if self.filename.endswith("c") or self.filename.endswith("o"): self.filename = self.filename[:-1] self.handle = win32file.FindFirstChangeNotification( os.path.dirname(self.filename), False, # watch tree? win32con.FILE_NOTIFY_CHANGE_LAST_WRITE) threading.Thread.__init__(self) def run(self): last_time = os.stat(self.filename)[stat.ST_MTIME] while 1: try: rc = win32event.WaitForSingleObject(self.handle, win32event.INFINITE) win32file.FindNextChangeNotification(self.handle) except win32event.error, details: # handle closed - thread should terminate. if details[0] != winerror.ERROR_INVALID_HANDLE: raise break this_time = os.stat(self.filename)[stat.ST_MTIME] if this_time != last_time: print "Detected file change - flagging for reload." self.change_detected = True last_time = this_time def stop(self): win32file.FindCloseChangeNotification(self.handle) # The ISAPI extension - handles requests in our virtual dir, and sends the # response to the client. class Extension(SimpleExtension): "Python advanced sample Extension" def __init__(self): self.reload_watcher = ReloadWatcherThread() self.reload_watcher.start() def HttpExtensionProc(self, ecb): # NOTE: If you use a ThreadPoolExtension, you must still perform # this check in HttpExtensionProc - raising the exception from # The "Dispatch" method will just cause the exception to be # rendered to the browser. if self.reload_watcher.change_detected: print "Doing reload" raise InternalReloadException ecb.SendResponseHeaders("200 OK", "Content-Type: text/html\r\n\r\n", 0) print >> ecb, "<HTML><BODY>" print >> ecb, "This module has been imported" print >> ecb, "%d times" % (reload_counter,) print >> ecb, "</BODY></HTML>" ecb.close() return isapicon.HSE_STATUS_SUCCESS def TerminateExtension(self, status): self.reload_watcher.stop() # The entry points for the ISAPI extension. def __ExtensionFactory__(): return Extension() # Our special command line customization. # Pre-install hook for our virtual directory. def PreInstallDirectory(params, options): # If the user used our special '--description' option, # then we override our default. if options.description: params.Description = options.description # Post install hook for our entire script def PostInstall(params, options): print print "The sample has been installed." print "Point your browser to /AdvancedPythonSample" print "If you modify the source file and reload the page," print "you should see the reload counter increment" # Handler for our custom 'status' argument. def status_handler(options, log, arg): "Query the status of something" print "Everything seems to be fine!" custom_arg_handlers = {"status": status_handler} if __name__=='__main__': # If run from the command-line, install ourselves. from isapi.install import * params = ISAPIParameters(PostInstall = PostInstall) # Setup the virtual directories - this is a list of directories our # extension uses - in this case only 1. # Each extension has a "script map" - this is the mapping of ISAPI # extensions. sm = [ ScriptMapParams(Extension="*", Flags=0) ] vd = VirtualDirParameters(Name="AdvancedPythonSample", Description = Extension.__doc__, ScriptMaps = sm, ScriptMapUpdate = "replace", # specify the pre-install hook. PreInstall = PreInstallDirectory ) params.VirtualDirs = [vd] # Setup our custom option parser. from optparse import OptionParser parser = OptionParser('') # black usage, so isapi sets it. parser.add_option("", "--description", action="store", help="custom description to use for the virtual directory") HandleCommandLine(params, opt_parser=parser, custom_arg_handlers = custom_arg_handlers) --- NEW FILE: redirector.py --- # This is a sample configuration file for an ISAPI filter and extension # written in Python. # # Please see README.txt in this directory, and specifically the # information about the "loader" DLL - installing this sample will create # "_redirector.dll" in the current directory. The readme explains this. # Executing this script (or any server config script) will install the extension # into your web server. As the server executes, the PyISAPI framework will load # this module and create your Extension and Filter objects. # This sample provides a very simple redirector. # It is implemented by a filter and an extension. # * The filter is installed globally, as all filters are. # * A Virtual Directory named "python" is setup. This dir has our ISAPI # extension as the only application, mapped to file-extension '*'. Thus, our # extension handles *all* requests in this directory. # The basic process is that the filter does URL rewriting, redirecting every # URL to our Virtual Directory. Our extension then handles this request, # forwarding the data from the proxied site. # So, the process is: # * URL of "index.html" comes in. # * Filter rewrites this to "/python/index.html" # * Our extension sees the full "/python/index.html", removes the leading # portion, and opens and forwards the remote URL. # This sample is very small - it avoid most error handling, etc. It is for # demonstration purposes only. from isapi import isapicon, threaded_extension from isapi.simple import SimpleFilter import sys import traceback import urllib # sys.isapidllhandle will exist when we are loaded by the IIS framework. # In this case we redirect our output to the win32traceutil collector. if hasattr(sys, "isapidllhandle"): import win32traceutil # The site we are proxying. proxy = "http://www.python.org" # The name of the virtual directory we install in, and redirect from. virtualdir = "/python" # The ISAPI extension - handles requests in our virtual dir, and sends the # response to the client. class Extension(threaded_extension.ThreadPoolExtension): "Python sample Extension" def Dispatch(self, ecb): # Note that our ThreadPoolExtension base class will catch exceptions # in our Dispatch method, and write the traceback to the client. # That is perfect for this sample, so we don't catch our own. #print 'IIS dispatching "%s"' % (ecb.GetServerVariable("URL"),) url = ecb.GetServerVariable("URL") if url.startswith(virtualdir): new_url = proxy + url[len(virtualdir):] print "Opening", new_url fp = urllib.urlopen(new_url) headers = fp.info() ecb.SendResponseHeaders("200 OK", str(headers) + "\r\n", False) ecb.WriteClient(fp.read()) ecb.DoneWithSession() print "Returned data from '%s'!" % (new_url,) else: # this should never happen - we should only see requests that # start with our virtual directory name. print "Not proxying '%s'" % (url,) # The ISAPI filter. class Filter(SimpleFilter): "Sample Python Redirector" filter_flags = isapicon.SF_NOTIFY_PREPROC_HEADERS | \ isapicon.SF_NOTIFY_ORDER_DEFAULT def HttpFilterProc(self, fc): #print "Filter Dispatch" nt = fc.NotificationType assert nt == isapicon.SF_NOTIFY_PREPROC_HEADERS pp = fc.GetData() url = pp.GetHeader("url") #print "URL is '%s'" % (url,) prefix = virtualdir if not url.startswith(prefix): new_url = prefix + url print "New proxied URL is '%s'" % (new_url,) pp.SetHeader("url", new_url) return isapicon.SF_STATUS_REQ_HANDLED_NOTIFICATION; else: print "Filter ignoring URL '%s'" % (url,) # Some older code that handled SF_NOTIFY_URL_MAP. #~ print "Have URL_MAP notify" #~ urlmap = fc.GetData() #~ print "URI is", urlmap.URL #~ print "Path is", urlmap.PhysicalPath #~ if urlmap.URL.startswith("/UC/"): #~ # Find the /UC/ in the physical path, and nuke it (except #~ # as the path is physical, it is \) #~ p = urlmap.PhysicalPath #~ pos = p.index("\\UC\\") #~ p = p[:pos] + p[pos+3:] #~ p = r"E:\src\pyisapi\webroot\PyTest\formTest.htm" #~ print "New path is", p #~ urlmap.PhysicalPath = p # The entry points for the ISAPI extension. def __FilterFactory__(): return Filter() def __ExtensionFactory__(): return Extension() if __name__=='__main__': # If run from the command-line, install ourselves. from isapi.install import * params = ISAPIParameters() # Setup all filters - these are global to the site. params.Filters = [ FilterParameters(Name="PythonRedirector", Description=Filter.__doc__), ] # Setup the virtual directories - this is a list of directories our # extension uses - in this case only 1. # Each extension has a "script map" - this is the mapping of ISAPI # extensions. sm = [ ScriptMapParams(Extension="*", Flags=0) ] vd = VirtualDirParameters(Name=virtualdir[1:], Description = Extension.__doc__, ScriptMaps = sm, ScriptMapUpdate = "replace" ) params.VirtualDirs = [vd] HandleCommandLine(params) |
From: Mark H. <mha...@us...> - 2004-10-06 05:12:04
|
Update of /cvsroot/pywin32/pywin32/isapi/src In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17001/isapi/src Added Files: ControlBlock.h FilterContext.h PyExtensionObjects.cpp PyExtensionObjects.h PyFilterObjects.cpp PyFilterObjects.h PythonEng.cpp PythonEng.h StdAfx.cpp StdAfx.h Utils.cpp Utils.h pyISAPI.cpp pyISAPI.h Log Message: Rename pyisapi directory to isapi, so it matches what the user sees, as part of the process of integrating it with the rest of the build. --- NEW FILE: ControlBlock.h --- #ifndef __CONTROL_BLOCK_H__ #define __CONTROL_BLOCK_H__ // wrapper for the session context (ECB block for IIS) // If we move away from IIS these will have to change class CControlBlock { public: CControlBlock(EXTENSION_CONTROL_BLOCK *pECB=NULL) : m_pECB(pECB) { } ~CControlBlock() { } CControlBlock(const CControlBlock & rhs) { Copy(rhs); } CControlBlock & operator=(const CControlBlock & rhs) { if (this != &rhs) Copy(rhs); return *this; } // member retrieval functions EXTENSION_CONTROL_BLOCK * GetECB(void) { return m_pECB; } // wrappers for IIS ECB structures void SetStatus(const DWORD status) { m_pECB->dwHttpStatusCode = status; } void SetLogMessage(LPCTSTR msg) { strncpy(m_pECB->lpszLogData, msg, HSE_LOG_BUFFER_LEN); } DWORD WriteHeaders(LPCTSTR szStatus, LPCTSTR szHeader, const bool bKeepAlive=true) { // NOTE we must send Content-Length header with correct byte count // in order for keep-alive to work, the bKeepAlive flag is not enough // by itself.. HSE_SEND_HEADER_EX_INFO SendHeaderExInfo; DWORD cchStatus = lstrlen(szStatus); DWORD cchHeader = lstrlen(szHeader); // Populate SendHeaderExInfo struct SendHeaderExInfo.pszStatus = szStatus; SendHeaderExInfo.pszHeader = szHeader; SendHeaderExInfo.cchStatus = cchStatus; SendHeaderExInfo.cchHeader = cchHeader; SendHeaderExInfo.fKeepConn = (bKeepAlive) ? TRUE:FALSE; // Send header return m_pECB->ServerSupportFunction(m_pECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX, &SendHeaderExInfo, NULL,NULL); } DWORD WriteStream(LPCTSTR buffer, const int buffLen, const int reserved=0) { DWORD dwBufLen = buffLen; m_pECB->WriteClient(m_pECB->ConnID, (void *) buffer, &dwBufLen, reserved); return dwBufLen; } bool ReadClient(LPVOID lpvBuffer, LPDWORD lpdwSize) { return m_pECB->ReadClient(m_pECB->ConnID, lpvBuffer, lpdwSize) ? true : false; } void DoneWithSession(DWORD dwState) { // Let IIS know that this worker thread is done with this request. This will allow // IIS to recycle the EXTENSION_CONTROL_BLOCK. m_pECB->ServerSupportFunction(m_pECB->ConnID, HSE_REQ_DONE_WITH_SESSION, &dwState, NULL, 0); } bool GetServerVariable(LPCTSTR varName, LPSTR lpBuff, DWORD *pBuffSize) { BOOL bOK = m_pECB->GetServerVariable(m_pECB->ConnID,(LPSTR) varName,lpBuff,pBuffSize); if (!bOK) *pBuffSize = 0; if (lpBuff[(*pBuffSize)-1]=='\0') (*pBuffSize)--; return (bOK) ? true : false; } bool GetImpersonationToken(HANDLE *ret) { return (m_pECB->ServerSupportFunction)( m_pECB->ConnID, HSE_REQ_GET_IMPERSONATION_TOKEN, ret, 0,0) ? true : false; } BOOL Redirect(LPCTSTR url) { DWORD buffSize = strlen(url); BOOL bOK = (m_pECB->ServerSupportFunction)( m_pECB->ConnID, HSE_REQ_SEND_URL_REDIRECT_RESP, (LPSTR) url, &buffSize,0); return bOK; } bool IsKeepAlive(void) { bool bKeepAlive = false; char buf[256]; DWORD bufsize = sizeof(buf)/sizeof(buf[0]); if (GetServerVariable("HTTP_CONNECTION",buf, &bufsize)){ bKeepAlive = strcmpi(buf, "keep-alive")==0; } return bKeepAlive; } private: EXTENSION_CONTROL_BLOCK * m_pECB; // IIS control block private: void Copy(const CControlBlock & rhs) { m_pECB = rhs.m_pECB; } }; #endif // __CONTROL_BLOCK_H__ --- NEW FILE: Utils.h --- /* ====================================================================== Copyright 2002-2003 by Blackdog Software Pty Ltd. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Blackdog Software not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. BLACKDOG SOFTWARE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL BLACKDOG SOFTWARE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ====================================================================== */ #ifndef __UTILS_H #define __UTILS_H // --------------------------------------------------------------------------- // Class: CSLock // Locking class which handles the serialisation of objects using CSingleLock. // class CSLock { public: inline CSLock(CRITICAL_SECTION &sem) : m_Lock(sem) { ::EnterCriticalSection(&m_Lock); } ~CSLock() { ::LeaveCriticalSection(&m_Lock); } private: CRITICAL_SECTION &m_Lock; }; // CSLock // Formats a system error code char *FormatSysError(const DWORD nErrNo); // Dump out an HTML error response page char *HTMLErrorResp(LPCTSTR msg); // returns the pathname of this module char *GetModulePath(void); #endif // __UTILS_H --- NEW FILE: PyFilterObjects.h --- /* ====================================================================== Copyright 2002-2003 by Blackdog Software Pty Ltd. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Blackdog Software not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. BLACKDOG SOFTWARE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL BLACKDOG SOFTWARE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ====================================================================== */ #ifndef __PyHFC_H #define __PyHFC_H #include "FilterContext.h" #include "structmember.h" #include "tupleobject.h" class PyFILTER_VERSION :public PyObject { HTTP_FILTER_VERSION * m_pfv; public: PyFILTER_VERSION(HTTP_FILTER_VERSION* pfv); ~PyFILTER_VERSION(); public: void Reset() {m_pfv = NULL;} // Python support static void deallocFunc(PyObject *ob); static PyObject *getattr(PyObject *self, char *name); static int setattr(PyObject *self, char *name, PyObject *v); }; class PyHFC :public PyObject { CFilterContext * m_pfc; public: PyHFC(CFilterContext* pfc = NULL); ~PyHFC(); void Reset() {m_pfc = NULL;} CFilterContext *GetFilterContext() {return m_pfc;} public: // Python support static void deallocFunc(PyObject *ob); static PyObject *getattr(PyObject *self, char *name); static int setattr(PyObject *self, char *name, PyObject *v); // class methods static PyObject * GetData(PyObject *self, PyObject *args); static PyObject * WriteClient(PyObject *self, PyObject *args); static PyObject * GetServerVariable(PyObject *self, PyObject *args); // ServerSupportFunction implemented functions. static PyObject * SendResponseHeader(PyObject *self, PyObject *args); protected: #pragma warning( disable : 4251 ) static struct memberlist PyHFC_memberlist[]; #pragma warning( default : 4251 ) DWORD m_notificationType; DWORD m_revision; BOOL m_isSecurePort; }; class PyURL_MAP :public PyObject { public: PyHFC *m_parent; public: PyURL_MAP(PyHFC *); ~PyURL_MAP(); HTTP_FILTER_URL_MAP *GetURLMap(); public: // Python support static void deallocFunc(PyObject *ob); static PyObject *getattr(PyObject *self, char *name); static int setattr(PyObject *self, char *name, PyObject *v); }; class PyPREPROC_HEADERS :public PyObject { public: PyHFC *m_parent; public: PyPREPROC_HEADERS(PyHFC *); ~PyPREPROC_HEADERS(); HTTP_FILTER_CONTEXT *GetFILTER_CONTEXT(); HTTP_FILTER_PREPROC_HEADERS *GetPREPROC_HEADERS(); public: // Python support static void deallocFunc(PyObject *ob); static PyObject *getattr(PyObject *self, char *name); static int setattr(PyObject *self, char *name, PyObject *v); }; // error handling static PyObject * PyHFC_Error = NULL; PyObject * SetPyHFCError(char *fnName, long err=0); #endif // __PyHFC_H --- NEW FILE: pyISAPI.cpp --- /* ====================================================================== Copyright 2002-2003 by Blackdog Software Pty Ltd. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Blackdog Software not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. BLACKDOG SOFTWARE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL BLACKDOG SOFTWARE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ====================================================================== */ // PYISAPI.CPP - Implementation file for your Internet Server // Python ISAPI Extension #include "stdafx.h" #include "pyISAPI.h" #include "pyExtensionObjects.h" #include "pyFilterObjects.h" static const char *name_ext_factory = "__ExtensionFactory__"; static const char *name_ext_init = "GetExtensionVersion"; static const char *name_ext_do = "HttpExtensionProc"; static const char *name_ext_term = "TerminateExtension"; static const char *name_filter_factory = "__FilterFactory__"; static const char *name_filter_init = "GetFilterVersion"; static const char *name_filter_do = "HttpFilterProc"; static const char *name_filter_term = "TerminateFilter"; static CPythonEngine pyEngine; static CPythonHandler filterHandler; static CPythonHandler extensionHandler; bool g_IsFrozen = false; char g_CallbackModuleName[_MAX_PATH + _MAX_FNAME] = ""; #define TRACE OutputDebugString // This is an entry point for py2exe. void WINAPI PyISAPISetOptions(const char *modname, BOOL is_frozen) { strncpy(g_CallbackModuleName, modname, sizeof(g_CallbackModuleName)/sizeof(g_CallbackModuleName[0])); // cast BOOL->bool without compiler warning! g_IsFrozen = is_frozen ? TRUE : FALSE; } BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer) { pVer->dwExtensionVersion = MAKELONG( HSE_VERSION_MINOR, HSE_VERSION_MAJOR ); // ensure our handler ready to go if (!extensionHandler.Init(&pyEngine, name_ext_factory, name_ext_init, name_ext_do, name_ext_term)) { // already have reported any errors to Python. TRACE("Unable to load Python handler"); return false; } PyObject *resultobject = NULL; bool bRetStatus = true; PyGILState_STATE state = PyGILState_Ensure(); // create the Python object PyVERSION_INFO *pyVO = new PyVERSION_INFO(pVer); resultobject = extensionHandler.Callback(HANDLER_INIT, "N", pyVO); if (! resultobject) { ExtensionError(NULL, "Extension version function failed!"); bRetStatus = false; } else { if (resultobject == Py_None) bRetStatus = TRUE; else if (PyInt_Check(resultobject)) bRetStatus = PyInt_AsLong(resultobject) ? true : false; else { ExtensionError(NULL, "Filter init should return an int, or None"); bRetStatus = FALSE; } } Py_XDECREF(resultobject); PyGILState_Release(state); return bRetStatus; } DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB) { DWORD result; PyGILState_STATE state = PyGILState_Ensure(); CControlBlock * pcb = new CControlBlock(pECB); PyECB *pyECB = new PyECB(pcb); PyObject *resultobject = extensionHandler.Callback(HANDLER_DO, "N", pyECB); if (! resultobject) { ExtensionError(pcb, "HttpExtensionProc function failed!"); result = HSE_STATUS_ERROR; } else { if (PyInt_Check(resultobject)) result = PyInt_AsLong(resultobject); else { ExtensionError(pcb, "HttpExtensionProc should return an int"); result = HSE_STATUS_ERROR; } } Py_XDECREF(resultobject); PyGILState_Release(state); return result; } BOOL WINAPI TerminateExtension(DWORD dwFlags) { // extension is being terminated BOOL bRetStatus; PyGILState_STATE state = PyGILState_Ensure(); PyObject *resultobject = extensionHandler.Callback(HANDLER_TERM, "i", dwFlags); if (! resultobject) { ExtensionError(NULL, "Extension term function failed!"); bRetStatus = false; } else { if (resultobject == Py_None) bRetStatus = TRUE; else if (PyInt_Check(resultobject)) bRetStatus = PyInt_AsLong(resultobject) ? true : false; else { ExtensionError(NULL, "Extension term should return an int, or None"); bRetStatus = FALSE; } } Py_XDECREF(resultobject); PyGILState_Release(state); extensionHandler.Term(); return bRetStatus; } BOOL WINAPI GetFilterVersion(HTTP_FILTER_VERSION *pVer) { pVer->dwFilterVersion = HTTP_FILTER_REVISION; // ensure our handler ready to go if (!filterHandler.Init(&pyEngine, name_filter_factory, name_filter_init, name_filter_do, name_filter_term)) // error already imported. return FALSE; PyGILState_STATE state = PyGILState_Ensure(); PyFILTER_VERSION *pyFV = new PyFILTER_VERSION(pVer); PyObject *resultobject = filterHandler.Callback(HANDLER_INIT, "N", pyFV); BOOL bRetStatus; if (! resultobject) { FilterError(NULL, "Filter version function failed!"); bRetStatus = false; } else { if (resultobject == Py_None) bRetStatus = TRUE; else if (PyInt_Check(resultobject)) bRetStatus = PyInt_AsLong(resultobject) ? true : false; else { FilterError(NULL, "Filter init should return an int, or None"); bRetStatus = FALSE; } } Py_XDECREF(resultobject); PyGILState_Release(state); return bRetStatus; } DWORD WINAPI HttpFilterProc(HTTP_FILTER_CONTEXT *phfc, DWORD NotificationType, VOID *pvData) { DWORD action; PyGILState_STATE state = PyGILState_Ensure(); PyObject *resultobject = NULL; // create the Python object CFilterContext fc(phfc, NotificationType, pvData); PyHFC *pyHFC = new PyHFC(&fc); resultobject = filterHandler.Callback(HANDLER_DO, "O", pyHFC); if (! resultobject) { FilterError(&fc, "Filter function failed!"); action = SF_STATUS_REQ_ERROR; } else { DWORD action; if (resultobject == Py_None) action = SF_STATUS_REQ_NEXT_NOTIFICATION; else if (PyInt_Check(resultobject)) action = PyInt_AsLong(resultobject); else { FilterError(&fc, "Filter should return an int, or None"); action = SF_STATUS_REQ_ERROR; } } pyHFC->Reset(); Py_DECREF(pyHFC); Py_XDECREF(resultobject); PyGILState_Release(state); return action; } BOOL WINAPI TerminateFilter(DWORD status) { BOOL bRetStatus; PyGILState_STATE state = PyGILState_Ensure(); PyObject *resultobject = filterHandler.Callback(HANDLER_TERM, "i", status); if (! resultobject) { FilterError(NULL, "Filter version function failed!"); bRetStatus = false; } else { if (resultobject == Py_None) bRetStatus = TRUE; else if (PyInt_Check(resultobject)) bRetStatus = PyInt_AsLong(resultobject) ? true : false; else { FilterError(NULL, "Filter term should return an int, or None"); bRetStatus = FALSE; } } Py_XDECREF(resultobject); PyGILState_Release(state); // filter is being terminated filterHandler.Term(); return bRetStatus; } /////////////////////////////////////////////////////////////////////// // If your extension will not use MFC, you'll need this code to make // sure the extension objects can find the resource handle for the // module. If you convert your extension to not be dependent on MFC, // remove the comments arounn the following AfxGetResourceHandle() // and DllMain() functions, as well as the g_hInstance global. HINSTANCE g_hInstance = 0; BOOL WINAPI DllMain(HINSTANCE hInst, ULONG ulReason, LPVOID lpReserved) { if (ulReason == DLL_PROCESS_ATTACH) { g_hInstance = hInst; } return TRUE; } --- NEW FILE: PythonEng.cpp --- /* ====================================================================== Copyright 2002-2003 by Blackdog Software Pty Ltd. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Blackdog Software not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. BLACKDOG SOFTWARE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL BLACKDOG SOFTWARE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ====================================================================== */ // NOTE: This code used to host the thread-pool used by dispatch to Python // Some of the methods etc made alot more sense then. #include "stdafx.h" #include "Utils.h" #include "PythonEng.h" #include "pyExtensionObjects.h" #include "pyFilterObjects.h" extern HINSTANCE g_hInstance; extern bool g_IsFrozen; extern char g_CallbackModuleName[_MAX_PATH + _MAX_FNAME]; ///////////////////////////////////////////////////////////////////// // Python Engine ///////////////////////////////////////////////////////////////////// CRITICAL_SECTION CPythonEngine::m_initLock; bool CPythonEngine::m_haveInit = false; PyObject * CPythonEngine::m_reload_exception = NULL; CPythonEngine::CPythonEngine() { InitializeCriticalSection(&m_initLock); } CPythonEngine::~CPythonEngine() { DeleteCriticalSection(&m_initLock); } bool CPythonEngine::InitMainInterp(void) { // ensure only 1 engine/thread initialises this only CSLock l(m_initLock); if (!m_haveInit) { PyGILState_STATE old_state; if (Py_IsInitialized()) old_state = PyGILState_Ensure(); else { Py_Initialize(); old_state = PyGILState_UNLOCKED; } PyEval_InitThreads(); if (!g_IsFrozen) { char *dll_path = GetModulePath(); AddToPythonPath(dll_path); free(dll_path); PyErr_Clear(); } // isapidllhandle to match dllhandle, frozendllhandle, etc :) Also a // nice way for a program to know they are in an ISAPI context. PyObject *obh = PyLong_FromVoidPtr(g_hInstance); PySys_SetObject("isapidllhandle", obh); Py_XDECREF(obh); // Locate the special exception we use to trigger a reload. PyObject *isapi_package = PyImport_ImportModule("isapi"); if (isapi_package) m_reload_exception = PyObject_GetAttrString(isapi_package, "InternalReloadException"); Py_XDECREF(isapi_package); PyGILState_Release(old_state); FindModuleName(); m_haveInit = true; } return true; } void CPythonEngine::FindModuleName() { TCHAR szFilePath[_MAX_PATH]; TCHAR szBase[_MAX_FNAME]; TCHAR *module_name; // If a name for the module has been magically setup (eg, via a frozen // app), then use it. Otherwise, assume it is the DLL name without the // first character (ie, without the leading _) if (g_CallbackModuleName && *g_CallbackModuleName) module_name = g_CallbackModuleName; else { // find out where our DLL/EXE module lives // NOTE: the long file name does not get returned (don't know why) ::GetModuleFileName(g_hInstance, szFilePath, sizeof(szFilePath)); ::_splitpath( szFilePath, NULL, NULL, szBase, NULL); module_name = szBase + 1; // skip first char. } strncpy(m_module_name, module_name, sizeof(m_module_name)/sizeof(m_module_name[0])); } bool CPythonEngine::AddToPythonPath(LPCTSTR pPathName) { PyObject *obPathList = PySys_GetObject(_T("path")); if (obPathList==NULL) { return false; } PyObject *obNew = PyString_FromString(pPathName); if (obNew==NULL) { return false; } bool bFnd=false; for (int i=0; i<PyList_Size(obPathList); i++){ PyObject * obItem = PyList_GetItem(obPathList, i); if(PyObject_Compare(obNew, obItem) == 0){ bFnd = true; break; } } if (!bFnd) PyList_Insert(obPathList, 0, obNew); Py_XDECREF(obNew); return true; } /////////////////////////////////////////////////////////////////////// // // The callback manager // CPythonHandler::CPythonHandler() : m_namefactory(0), m_nameinit(0), m_namedo(0), m_nameterm(0), m_callback_init(0), m_callback_do(0), m_callback_term(0), m_handler(0) { return; } bool CPythonHandler::Init( CPythonEngine *engine, const char *factory, const char *nameinit, const char *namedo, const char *nameterm) { if (!engine->InitMainInterp()) return false; m_nameinit = nameinit; m_namedo = namedo; m_nameterm = nameterm; m_namefactory = factory; m_engine = engine; return LoadHandler(false); } bool CPythonHandler::LoadHandler(bool reload) { char szErrBuf[1024]; PyObject *m; PyGILState_STATE old_state = PyGILState_Ensure(); m = PyImport_ImportModule(m_engine->m_module_name); if (m && reload) { PyObject *m_orig = m; m = PyImport_ReloadModule(m); Py_DECREF(m_orig); } if (!m) { _snprintf(szErrBuf, sizeof(szErrBuf)/sizeof(szErrBuf[0]), "Failed to import callback module '%s'", m_engine->m_module_name); ExtensionError(NULL, szErrBuf); } if (m) { Py_XDECREF(m_handler); if (!((m_handler = PyObject_CallMethod(m, (char *)m_namefactory, NULL)))) { _snprintf(szErrBuf, sizeof(szErrBuf)/sizeof(szErrBuf[0]), "Factory function '%s' failed", m_namefactory); ExtensionError(NULL, szErrBuf); } Py_DECREF(m); } PyGILState_Release(old_state); return m_handler != NULL; } bool CPythonHandler::CheckCallback(const char *cbname, PyObject **cb) { if (*cb!=NULL) return true; // already have the callback. PyGILState_STATE old_state = PyGILState_Ensure(); if (!m_handler) { PyErr_SetString(PyExc_RuntimeError, "The handler failed to load"); return false; } *cb = PyObject_GetAttrString(m_handler, (char *)cbname); if (!*cb) ExtensionError(NULL, "Failed to locate the callback"); PyGILState_Release(old_state); return (*cb) != NULL; } // NOTE: Caller must setup and release thread-state - as we return a PyObject, // the caller must at least Py_DECREF it, so must hold the lock. PyObject *CPythonHandler::DoCallback( HANDLER_TYPE typ, PyObject *args ) { PyObject **ppcb; const char *cb_name; switch(typ) { case HANDLER_INIT: ppcb = &m_callback_init; cb_name = m_nameinit; break; case HANDLER_TERM: ppcb = &m_callback_term; cb_name = m_nameterm; break; default: ppcb = &m_callback_do; cb_name = m_namedo; break; } if (!CheckCallback(cb_name, ppcb)) return NULL; return PyObject_Call(*ppcb, args, NULL); } PyObject *CPythonHandler::Callback( HANDLER_TYPE typ, const char *format /* = NULL */, ... ) { va_list va; PyObject *args; if (format && *format) { va_start(va, format); args = Py_VaBuildValue((char *)format, va); va_end(va); } else args = PyTuple_New(0); if (args == NULL) return NULL; if (!PyTuple_Check(args)) { PyObject *a; a = PyTuple_New(1); if (a == NULL) return NULL; if (PyTuple_SetItem(a, 0, args) < 0) return NULL; args = a; } PyObject *ret = DoCallback(typ, args); if (!ret) { if (m_engine->m_reload_exception && PyErr_ExceptionMatches(m_engine->m_reload_exception)) { PyErr_Clear(); // Need to call term first PyObject *temp_args = Py_BuildValue("(i)", 0); ret = DoCallback(HANDLER_TERM, temp_args); Py_XDECREF(temp_args); if (!ret) { ExtensionError(NULL, "Terminating for reload failed"); PyErr_Clear(); } Py_XDECREF(ret); // Now force the reload and refresh of all callbacks. if (!LoadHandler(true)) return NULL; Py_XDECREF(m_callback_init); m_callback_init = NULL; Py_XDECREF(m_callback_do); m_callback_do = NULL; Py_XDECREF(m_callback_term); m_callback_term = NULL; // call init again temp_args = Py_BuildValue("(z)", NULL); ret = DoCallback(HANDLER_INIT, temp_args); Py_XDECREF(temp_args); if (!ret) { ExtensionError(NULL, "Reinitializing after import failed"); PyErr_Clear(); } Py_XDECREF(ret); // And make the original call again. ret = DoCallback(typ, args); } } Py_DECREF(args); return ret; } void CPythonHandler::Term(void) { // never shut down - Python leaks badly and has other // side effects if you repeatedly Init then Term Py_XDECREF(m_callback_init); Py_XDECREF(m_callback_do); Py_XDECREF(m_callback_term); } ////////////////////////////////////////////////////////////////////////////// // general error handler void ExtensionError(CControlBlock *pcb, LPCTSTR errmsg) { char *windows_error = ::GetLastError() ? ::FormatSysError(::GetLastError()) : NULL; PyGILState_STATE s = PyGILState_Ensure(); PySys_WriteStderr("Internal Extension Error: %s\n", errmsg); if (windows_error) PySys_WriteStderr("Last Windows error: %s\n", windows_error); if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } PyGILState_Release(s); if (pcb) { char *htmlStream = HTMLErrorResp(errmsg); pcb->SetStatus(HSE_STATUS_ERROR); pcb->SetLogMessage(errmsg); pcb->WriteHeaders(_T("200 OK"), _T("Content-type: text/html\r\n\r\n"), false); pcb->WriteStream(htmlStream, strlen(htmlStream)); if (windows_error) { static char *chunk = "<br>Last Windows error:"; pcb->WriteStream(chunk, strlen(chunk)); pcb->WriteStream(windows_error, strlen(windows_error)); } } if (windows_error) free(windows_error); } void FilterError(CFilterContext *pfc, LPCTSTR errmsg) { PyGILState_STATE s = PyGILState_Ensure(); PySys_WriteStderr("Internal Filter Error: %s\n", errmsg); if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } PyGILState_Release(s); // what else to do here? AddResponseHeaders->WriteClient? } --- NEW FILE: PyExtensionObjects.h --- /* ====================================================================== Copyright 2002-2003 by Blackdog Software Pty Ltd. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Blackdog Software not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. BLACKDOG SOFTWARE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL BLACKDOG SOFTWARE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ====================================================================== */ #ifndef __PyExtensionObjects_H__ #define __PyExtensionObjects_H__ #include "ControlBlock.h" #include "structmember.h" #include "tupleobject.h" class PyVERSION_INFO :public PyObject { HSE_VERSION_INFO * m_pvi; public: PyVERSION_INFO(HSE_VERSION_INFO* pfv); ~PyVERSION_INFO(); public: void Reset() {m_pvi = NULL;} // Python support static void deallocFunc(PyObject *ob); static PyObject *getattr(PyObject *self, char *name); static int setattr(PyObject *self, char *name, PyObject *v); }; class PyECB :public PyObject { CControlBlock * m_pcb; DWORD m_version; // Version info of this spec HCONN m_connID; // Context number not to be modified! PyObject * m_method; // REQUEST_METHOD PyObject * m_queryString; // QUERY_STRING PyObject * m_pathInfo; // PATH_INFO PyObject * m_pathTranslated; // PATH_TRANSLATED DWORD m_totalBytes; // Total bytes indicated from client DWORD m_available; // Available number of bytes PyObject * m_data; // Pointer to cbAvailable bytes PyObject * m_contentType; // Content type of client data DWORD m_HttpStatusCode; // The status of the current transaction when the request is completed. PyObject * m_logData; // log data string bool m_bAsyncDone; // sent the async done public: PyECB(CControlBlock * pcb = NULL); ~PyECB(); bool FinishedResponse(void) { return m_bAsyncDone; } public: // Python support static void deallocFunc(PyObject *ob); static PyObject *getattr(PyObject *self, char *name); static int setattr(PyObject *self, char *name, PyObject *v); // class methods static PyObject * WriteClient(PyObject *self, PyObject *args); static PyObject * GetServerVariable(PyObject *self, PyObject *args); static PyObject * ReadClient(PyObject *self, PyObject *args); // Server support function wrappers // these wrap the various server support functions supported through the // ServerSupportFunction routine. static PyObject * SendResponseHeaders(PyObject *self, PyObject * args); // HSE_REQ_SEND_RESPONSE_HEADER_EX static PyObject * Redirect(PyObject *self, PyObject * args); // HSE_REQ_SEND_URL_REDIRECT_RESP. static PyObject * IsKeepAlive(PyObject *self, PyObject * args); // Keep alive flag set static PyObject * DoneWithSession(PyObject *self, PyObject * args); //HSE_REQ_DONE_WITH_SESSION static PyObject * GetImpersonationToken(PyObject *self, PyObject * args); // HSE_REQ_GET_IMPERSONATION_TOKEN static PyObject * IsSessionActive(PyObject *self, PyObject * args); protected: #pragma warning( disable : 4251 ) static struct memberlist PyECB_memberlist[]; #pragma warning( default : 4251 ) }; // error handling static PyObject * PyECB_Error = NULL; PyObject * SetPyECBError(char *fnName, long err=0); #endif // __PyExtensionObjects_H__ --- NEW FILE: PyFilterObjects.cpp --- /* ====================================================================== Copyright 2002-2003 by Blackdog Software Pty Ltd. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Blackdog Software not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. BLACKDOG SOFTWARE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL BLACKDOG SOFTWARE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ====================================================================== */ #include "stdafx.h" #include "Utils.h" #include "pyFilterObjects.h" // @doc // @object HTTP_FILTER_VERSION|A Python interface to the ISAPI HTTP_FILTER_VERSION // structure. PyTypeObject PyFILTER_VERSIONType = { PyObject_HEAD_INIT(&PyType_Type) 0, "HTTP_FILTER_VERSION", sizeof(PyFILTER_VERSION), 0, PyFILTER_VERSION::deallocFunc, /* tp_dealloc */ 0, /* tp_print */ PyFILTER_VERSION::getattr, /* tp_getattr */ PyFILTER_VERSION::setattr, /* tp_setattr */ 0, 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, 0, /* tp_call */ 0, /* tp_str */ }; PyFILTER_VERSION::PyFILTER_VERSION(HTTP_FILTER_VERSION *pfv) { ob_type = &PyFILTER_VERSIONType; _Py_NewReference(this); m_pfv = pfv; } PyFILTER_VERSION::~PyFILTER_VERSION() { } PyObject *PyFILTER_VERSION::getattr(PyObject *self, char *name) { PyFILTER_VERSION *me = (PyFILTER_VERSION *)self; if (!me->m_pfv) return PyErr_Format(PyExc_RuntimeError, "FILTER_VERSION structure no longer exists"); // @prop int|ServerFilterVersion|(read-only) if (strcmp(name, "ServerFilterVersion")==0) { return PyInt_FromLong(me->m_pfv->dwServerFilterVersion); } // @prop int|FilterVersion| if (strcmp(name, "FilterVersion")==0) { return PyInt_FromLong(me->m_pfv->dwFilterVersion); } // @prop int|Flags| if (strcmp(name, "Flags")==0) { return PyInt_FromLong(me->m_pfv->dwFlags); } // @prop string|FilterDesc| if (strcmp(name, "FilterDesc")==0) { return PyString_FromString(me->m_pfv->lpszFilterDesc); } return PyErr_Format(PyExc_AttributeError, "PyFILTER_VERSION has no attribute '%s'", name); } int PyFILTER_VERSION::setattr(PyObject *self, char *name, PyObject *v) { PyFILTER_VERSION *me = (PyFILTER_VERSION *)self; if (!me->m_pfv) { PyErr_Format(PyExc_RuntimeError, "FILTER_VERSION structure no longer exists"); return -1; } if (v == NULL) { PyErr_SetString(PyExc_AttributeError, "can't delete FILTER_VERSION attributes"); return -1; } if (strcmp(name, "FilterVersion")==0) { if (!PyInt_Check(v)) { PyErr_Format(PyExc_ValueError, "FilterVersion must be an int (got %s)", v->ob_type->tp_name); return -1; } me->m_pfv->dwFilterVersion = PyInt_AsLong(v); } else if (strcmp(name, "Flags")==0) { if (!PyInt_Check(v)) { PyErr_Format(PyExc_ValueError, "Flags must be an int (got %s)", v->ob_type->tp_name); return -1; } me->m_pfv->dwFlags = PyInt_AsLong(v); } else if (strcmp(name, "FilterDesc")==0) { if (!PyString_Check(v)) { PyErr_Format(PyExc_ValueError, "FilterDesc must be a string (got %s)", v->ob_type->tp_name); return -1; } if (PyString_Size(v) > SF_MAX_FILTER_DESC_LEN) { PyErr_Format(PyExc_ValueError, "String is too long - max of %d chars", SF_MAX_FILTER_DESC_LEN); return -1; } strcpy(me->m_pfv->lpszFilterDesc, PyString_AsString(v)); } else { PyErr_SetString(PyExc_AttributeError, "can't modify read only FILTER_VERSION attributes."); return -1; } return 0; } void PyFILTER_VERSION::deallocFunc(PyObject *ob) { delete (PyFILTER_VERSION *)ob; } ///////////////////////////////////////////////////////////////////// // filter context wrapper ///////////////////////////////////////////////////////////////////// #define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0])) #define ECBOFF(e) offsetof(PyHFC, e) // @pymethod object|HTTP_FILTER_CONTEXT|GetData|Obtains the data passed to // The HttpFilterProc function. This is not techinally part of the // HTTP_FILTER_CONTEXT structure, but packaged here for convenience. PyObject * PyHFC::GetData(PyObject *self, PyObject *args) { PyHFC *me = (PyHFC *)self; // @rdesc The result depends on the value of <om HTTP_FILTER_CONTEXT.NotificationType> // @flagh NotificationType|Result type switch (me->m_notificationType) { // @flag SF_NOTIFY_URL_MAP|<o HTTP_FILTER_URL_MAP> case SF_NOTIFY_URL_MAP: return new PyURL_MAP(me); // @flag SF_NOTIFY_PREPROC_HEADERS|<o HTTP_FILTER_PREPROC_HEADERS> case SF_NOTIFY_PREPROC_HEADERS: return new PyPREPROC_HEADERS(me); default: PyErr_Format(PyExc_ValueError, "Don't understand data of type 0x%x", me->m_notificationType); return NULL; } /* not reached */ assert(false); } // @pymethod |HTTP_FILTER_CONTEXT|WriteClient| PyObject * PyHFC::WriteClient(PyObject *self, PyObject *args) { BOOL bRes = FALSE; TCHAR * buffer = NULL; int buffLen = 0; int reserved = 0; PyHFC * phfc = (PyHFC *) self; // @pyparm string|data|| // @pyparm int|reserverd|0| if (!PyArg_ParseTuple(args, "s#|l:WriteClient", &buffer, &buffLen, &reserved)) return NULL; if (phfc->m_pfc){ Py_BEGIN_ALLOW_THREADS bRes = phfc->m_pfc->WriteClient(buffer, buffLen, reserved); Py_END_ALLOW_THREADS if (!bRes) return SetPyHFCError("WriteClient"); } Py_INCREF(Py_None); return Py_None; } // @pymethod string|HTTP_FILTER_CONTEXT|GetServerVariable| PyObject * PyHFC::GetServerVariable(PyObject *self, PyObject *args) { BOOL bRes = FALSE; TCHAR * variable = NULL; PyObject *def = NULL; PyHFC * phfc = (PyHFC *) self; // @pyparm string|variable|| // @pyparm object|default||If specified, the function will return this // value instead of raising an error if the variable could not be fetched. if (!PyArg_ParseTuple(args, "s|O:GetServerVariable", &variable, &def)) return NULL; char buf[8192] = ""; DWORD bufsize = sizeof(buf)/sizeof(buf[0]); if (phfc->m_pfc){ Py_BEGIN_ALLOW_THREADS bRes = phfc->m_pfc->GetServerVariable(variable, buf, &bufsize); Py_END_ALLOW_THREADS if (!bRes) { if (def) { Py_INCREF(def); return def; } return SetPyHFCError("GetServerVariable"); } } return PyString_FromStringAndSize(buf, bufsize); } // @pymethod |HTTP_FILTER_CONTEXT|SendResponseHeader| PyObject * PyHFC::SendResponseHeader(PyObject *self, PyObject *args) { BOOL bRes = FALSE; char *status, *header; PyHFC * phfc = (PyHFC *) self; // @pyparm string|status|| // @pyparm string|header|| if (!PyArg_ParseTuple(args, "zz:SendResponseHeader", &status, &header)) return NULL; if (!phfc->m_pfc) return PyErr_Format(PyExc_RuntimeError, "No filtercontext!"); Py_BEGIN_ALLOW_THREADS // The Java code passes "\r\n" as first DWORD, and header in second, // but docs clearly have second as unused. Either way, I can't see the // specific header! bRes = phfc->m_pfc->ServerSupportFunction(SF_REQ_SEND_RESPONSE_HEADER, status, (DWORD)header, 0); Py_END_ALLOW_THREADS if (!bRes) return SetPyHFCError("SendResponseHeader"); Py_INCREF(Py_None); return Py_None; } // @object HTTP_FILTER_CONTEXT|A Python representation of an ISAPI // HTTP_FILTER_CONTEXT structure. static struct PyMethodDef PyHFC_methods[] = { {"GetData", PyHFC::GetData, 1}, // @pymeth GetData| {"GetServerVariable", PyHFC::GetServerVariable, 1}, // @pymeth GetServerVariable| {"WriteClient", PyHFC::WriteClient, 1}, // @pymeth WriteClient| {"write", PyHFC::WriteClient, 1}, // @pymeth write|A synonym for WriteClient, this allows you to 'print >> fc' {"SendResponseHeader", PyHFC::SendResponseHeader, 1}, // @pymeth SendResponseHeader| {NULL} }; struct memberlist PyHFC::PyHFC_memberlist[] = { // @prop int|Revision|(read-only) {"Revision", T_INT,ECBOFF(m_revision), READONLY}, // @prop bool|fIsSecurePort|(read-only) {"fIsSecurePort", T_INT, ECBOFF(m_isSecurePort), READONLY}, // @prop int|NotificationType|(read-only) {"NotificationType", T_INT,ECBOFF(m_notificationType), READONLY}, {NULL} }; PyTypeObject PyHFCType = { PyObject_HEAD_INIT(&PyType_Type) 0, "HTTP_FILTER_CONTEXT", sizeof(PyHFC), 0, PyHFC::deallocFunc, /* tp_dealloc */ 0, /* tp_print */ PyHFC::getattr, /* tp_getattr */ PyHFC::setattr, /* tp_setattr */ 0, 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, 0, /* tp_call */ 0, /* tp_str */ }; PyHFC::PyHFC(CFilterContext* pfc) { ob_type = &PyHFCType; _Py_NewReference(this); m_pfc = pfc; HTTP_FILTER_CONTEXT *phfc; VOID *pData; pfc->GetFilterData(&phfc, &m_notificationType, &pData); m_revision = phfc->Revision; m_isSecurePort = phfc->fIsSecurePort; } PyHFC::~PyHFC() { if (m_pfc) delete m_pfc; } PyObject *PyHFC::getattr(PyObject *self, char *name) { // see if its a member variable for (int i=0; i<ARRAYSIZE(PyHFC::PyHFC_memberlist); i++){ if (PyHFC::PyHFC_memberlist[i].name && _tcsicmp(name, PyHFC::PyHFC_memberlist[i].name) == 0) return PyMember_Get((char *)self, PyHFC::PyHFC_memberlist, name); } // see if its the special members attribute if (_tcscmp(name, _T("__members__"))==0) return PyMember_Get((char *)self, PyHFC::PyHFC_memberlist, name); // must be a method return Py_FindMethod(PyHFC_methods, self, name); } int PyHFC::setattr(PyObject *self, char *name, PyObject *v) { if (v == NULL) { PyErr_SetString(PyExc_AttributeError, "can't delete ECB attributes"); return -1; } PyErr_SetString(PyExc_AttributeError, "can't modify read only ECB attributes."); return -1; } void PyHFC::deallocFunc(PyObject *ob) { delete (PyHFC *)ob; } // Setup an exception PyObject * SetPyHFCError(char *fnName, long err /*= 0*/) { DWORD errorCode = err == 0 ? GetLastError() : err; if (PyHFC_Error==NULL) { PyObject *mod = PyImport_ImportModule("isapi"); if (mod) PyHFC_Error = PyObject_GetAttrString(mod, "FilterError"); else PyHFC_Error = PyExc_RuntimeError; // what's the alternative? Py_XDECREF(mod); } PyObject *v = Py_BuildValue("(izs)", errorCode, NULL, fnName); if (v != NULL) { PyErr_SetObject(PyHFC_Error, v); Py_DECREF(v); } return NULL; } ///////////////////////////////////////////////////////////////////////// // PyURL_MAP object ///////////////////////////////////////////////////////////////////////// // @object HTTP_FILTER_URL_MAP|A Python representation of an ISAPI // HTTP_FILTER_URL_MAP structure. PyTypeObject PyURL_MAPType = { PyObject_HEAD_INIT(&PyType_Type) 0, "HTTP_FILTER_URL_MAP", sizeof(PyURL_MAP), 0, PyURL_MAP::deallocFunc, /* tp_dealloc */ 0, /* tp_print */ PyURL_MAP::getattr, /* tp_getattr */ PyURL_MAP::setattr, /* tp_setattr */ 0, 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, 0, /* tp_call */ 0, /* tp_str */ }; PyURL_MAP::PyURL_MAP(PyHFC *pParent) { ob_type = &PyURL_MAPType; _Py_NewReference(this); m_parent = pParent; Py_INCREF(m_parent); } PyURL_MAP::~PyURL_MAP() { Py_XDECREF(m_parent); } HTTP_FILTER_URL_MAP *PyURL_MAP::GetURLMap() { HTTP_FILTER_CONTEXT *pFC; void *vdata; DWORD requestType; m_parent->GetFilterContext()->GetFilterData(&pFC, &requestType, &vdata); assert(requestType==SF_NOTIFY_URL_MAP); return (HTTP_FILTER_URL_MAP *)vdata; } PyObject *PyURL_MAP::getattr(PyObject *self, char *name) { HTTP_FILTER_URL_MAP *pMap = ((PyURL_MAP *)self)->GetURLMap(); if (!pMap) return NULL; // @prop string|URL| if (strcmp(name, "URL")==0) { return PyString_FromString(pMap->pszURL); } // @prop string|PhysicalPath| if (strcmp(name, "PhysicalPath")==0) { return PyString_FromString(pMap->pszPhysicalPath); } PyErr_Format(PyExc_AttributeError, "PyURL_MAP objects have no attribute '%s'", name); return NULL; } int PyURL_MAP::setattr(PyObject *self, char *name, PyObject *v) { HTTP_FILTER_URL_MAP *pMap = ((PyURL_MAP *)self)->GetURLMap(); if (!pMap) return NULL; if (strcmp(name, "PhysicalPath")==0) { if (!PyString_Check(v)) { PyErr_Format(PyExc_TypeError, "PhysicalPath must be a string"); return -1; } int cc = PyString_Size(v); if ((DWORD)cc >= pMap->cbPathBuff) { PyErr_Format(PyExc_ValueError, "The string is too long - got %d chars, but max is %d", cc, pMap->cbPathBuff-1); return -1; } _tcscpy(pMap->pszPhysicalPath, PyString_AS_STRING(v)); return 0; } PyErr_SetString(PyExc_AttributeError, "can't modify read only PyURL_MAP attributes."); return -1; } void PyURL_MAP::deallocFunc(PyObject *ob) { delete (PyURL_MAP *)ob; } ///////////////////////////////////////////////////////////////////////// // PyPREPROC_HEADERS object ///////////////////////////////////////////////////////////////////////// // @pymethod string|HTTP_FILTER_PREPROC_HEADERS|GetHeader| PyObject * PyPREPROC_HEADERS_GetHeader(PyObject *self, PyObject *args) { TCHAR buffer[8192]; DWORD bufSize = sizeof(buffer) / sizeof(TCHAR); char *name; // @pyparm string|header|| if (!PyArg_ParseTuple(args, "s:GetHeader", &name)) return NULL; BOOL ok; HTTP_FILTER_PREPROC_HEADERS *pp = ((PyPREPROC_HEADERS *)self)->GetPREPROC_HEADERS(); HTTP_FILTER_CONTEXT *pfc = ((PyPREPROC_HEADERS *)self)->GetFILTER_CONTEXT(); if (!pp || !pfc) return NULL; Py_BEGIN_ALLOW_THREADS ok = pp->GetHeader(pfc, name, buffer, &bufSize); Py_END_ALLOW_THREADS if (!ok) return SetPyHFCError("GetHeader"); return PyString_FromStringAndSize(buffer, bufSize-1); } // @pymethod |HTTP_FILTER_PREPROC_HEADERS|SetHeader| PyObject * PyPREPROC_HEADERS_SetHeader(PyObject *self, PyObject *args) { BOOL ok; char *name, *val; HTTP_FILTER_PREPROC_HEADERS *pp = ((PyPREPROC_HEADERS *)self)->GetPREPROC_HEADERS(); HTTP_FILTER_CONTEXT *pfc = ((PyPREPROC_HEADERS *)self)->GetFILTER_CONTEXT(); if (!pp || !pfc) return NULL; // @pyparm string|name|| // @pyparm string|val|| if (!PyArg_ParseTuple(args, "ss:SetHeader", &name, &val)) return NULL; Py_BEGIN_ALLOW_THREADS ok = pp->SetHeader(pfc, name, val); Py_END_ALLOW_THREADS if (!ok) return SetPyHFCError("SetHeader"); Py_INCREF(Py_None); return Py_None; } // @pymethod |HTTP_FILTER_PREPROC_HEADERS|AddHeader| PyObject * PyPREPROC_HEADERS_AddHeader(PyObject *self, PyObject *args) { BOOL ok; char *name, *val; HTTP_FILTER_PREPROC_HEADERS *pp = ((PyPREPROC_HEADERS *)self)->GetPREPROC_HEADERS(); HTTP_FILTER_CONTEXT *pfc = ((PyPREPROC_HEADERS *)self)->GetFILTER_CONTEXT(); if (!pp || !pfc) return NULL; if (!PyArg_ParseTuple(args, "ss:AddHeader", &name, &val)) return NULL; Py_BEGIN_ALLOW_THREADS ok = pp->AddHeader(pfc, name, val); Py_END_ALLOW_THREADS if (!ok) return SetPyHFCError("AddHeader"); Py_INCREF(Py_None); return Py_None; } // @object HTTP_FILTER_PREPROC_HEADERS|A Python representation of an ISAPI // HTTP_FILTER_PREPROC_HEADERS structure. static struct PyMethodDef PyPREPROC_HEADERS_methods[] = { {"GetHeader", PyPREPROC_HEADERS_GetHeader, 1}, // @pymeth GetHeader| {"SetHeader", PyPREPROC_HEADERS_SetHeader, 1}, // @pymeth SetHeader| {"AddHeader", PyPREPROC_HEADERS_AddHeader, 1}, // @pymeth AddHeader| {NULL} }; PyTypeObject PyPREPROC_HEADERSType = { PyObject_HEAD_INIT(&PyType_Type) 0, "HTTP_FILTER_PREPROC_HEADERS", sizeof(PyPREPROC_HEADERS), 0, PyPREPROC_HEADERS::deallocFunc, /* tp_dealloc */ 0, /* tp_print */ PyPREPROC_HEADERS::getattr, /* tp_getattr */ 0, /* tp_setattr */ 0, 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, 0, /* tp_call */ 0, /* tp_str */ }; PyPREPROC_HEADERS::PyPREPROC_HEADERS(PyHFC *pParent) { ob_type = &PyPREPROC_HEADERSType; _Py_NewReference(this); m_parent = pParent; Py_INCREF(m_parent); } PyPREPROC_HEADERS::~PyPREPROC_HEADERS() { Py_XDECREF(m_parent); } HTTP_FILTER_CONTEXT *PyPREPROC_HEADERS::GetFILTER_CONTEXT() { HTTP_FILTER_CONTEXT *pFC; m_parent->GetFilterContext()->GetFilterData(&pFC, NULL, NULL); return pFC; } HTTP_FILTER_PREPROC_HEADERS *PyPREPROC_HEADERS::GetPREPROC_HEADERS() { HTTP_FILTER_CONTEXT *pFC; void *vdata; DWORD requestType; m_parent->GetFilterContext()->GetFilterData(&pFC, &requestType, &vdata); assert(requestType==SF_NOTIFY_PREPROC_HEADERS); return (HTTP_FILTER_PREPROC_HEADERS *)vdata; } PyObject *PyPREPROC_HEADERS::getattr(PyObject *self, char *name) { return Py_FindMethod(PyPREPROC_HEADERS_methods, self, name); } void PyPREPROC_HEADERS::deallocFunc(PyObject *ob) { delete (PyPREPROC_HEADERS *)ob; } --- NEW FILE: pyISAPI.h --- /* ====================================================================== Copyright 2002-2003 by Blackdog Software Pty Ltd. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Blackdog Software not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. BLACKDOG SOFTWARE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL BLACKDOG SOFTWARE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ====================================================================== */ #ifndef __PYISAPI_H__ #define __PYISAPI_H__ // PYISAPI.H - Header file for your Internet Server // Python ISAPI Extension #include "PythonEng.h" BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer); DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB); BOOL WINAPI TerminateExtension(DWORD dwFlags); #endif // __PYISAPI_H__ --- NEW FILE: Utils.cpp --- /* ====================================================================== Copyright 2002-2003 by Blackdog Software Pty Ltd. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Blackdog Software not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. BLACKDOG SOFTWARE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL BLACKDOG SOFTWARE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ====================================================================== */ #include "stdafx.h" #include "Utils.h" extern HINSTANCE g_hInstance; // returns the pathname of this module char *GetModulePath(void) { // directory values TCHAR szFilePath[_MAX_PATH]; TCHAR szDrive[_MAX_DRIVE]; TCHAR szDir[_MAX_DIR]; // find out where the exe lives // NOTE: the long file name does not get returned (don't know why) ::GetModuleFileName(g_hInstance, szFilePath, sizeof(szFilePath)); ::_splitpath( szFilePath, szDrive, szDir, NULL, NULL ); int dir_len = strlen(szDir); if (dir_len && szDir[dir_len-1] == '\\') szDir[dir_len-1] = '\0'; char *result = (char *)malloc(strlen(szDrive)+strlen(szDir)+1); if (result) { strcpy(result, szDrive); strcat(result, szDir); } return result; } // Formats a system error code char *FormatSysError(const DWORD nErrNo) { // This should never happen, so we can be a little brutal. char *result = (char *)malloc(1024); if (!result) return NULL; int nLen =FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, nErrNo, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), result, 1024, NULL); if (nLen > 2) { if ( result[nLen - 1] == '\n') result[nLen - 1] = 0; if (result[nLen - 2] == '\r') result[nLen - 2] = 0; } return result; } // format an error char *HTMLErrorResp(LPCTSTR msg) { const char *htmlBody = "<html><head><title>Python ISAPI Error</title></head>" "<body><h2>An Error occured while processing your request</h2>" "<font color=\"Red\"> %s </font></body></html>"; // should not need the "+1" as the "%s" will be consumed, but... int newLen = strlen(htmlBody) + strlen(msg) + 1; char *result = (char *)malloc(newLen); if (result) sprintf(result, htmlBody, msg); return result; } --- NEW FILE: PyExtensionObjects.cpp --- /* ====================================================================== Copyright 2002-2003 by Blackdog Software Pty Ltd. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Blackdog Software not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. BLACKDOG SOFTWARE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL BLACKDOG SOFTWARE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ====================================================================== */ #include "stdafx.h" #include "Utils.h" #include "PyExtensionObjects.h" // @doc // @object HSE_VERSION_INFO|An object used by ISAPI GetExtensionVersion PyTypeObject PyVERSION_INFOType = { PyObject_HEAD_INIT(&PyType_Type) 0, "HSE_VERSION_INFO", sizeof(PyVERSION_INFO), 0, PyVERSION_INFO::deallocFunc, /* tp_dealloc */ 0, /* tp_print */ PyVERSION_INFO::getattr, /* tp_getattr */ PyVERSION_INFO::setattr, /* tp_setattr */ 0, 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, 0, /* tp_call */ 0, /* tp_str */ }; PyVERSION_INFO::PyVERSION_INFO(HSE_VERSION_INFO *pvi) { ob_type = &PyVERSION_INFOType; _Py_NewReference(this); m_pvi = pvi; } PyVERSION_INFO::~PyVERSION_INFO() { } PyObject *PyVERSION_INFO::getattr(PyObject *self, char *name) { PyVERSION_INFO *me = (PyVERSION_INFO *)self; if (!me->m_pvi) return PyErr_Format(PyExc_RuntimeError, "VERSION_INFO structure no longer exists"); if (strcmp(name, "ExtensionDesc")==0) { return PyString_FromString(me->m_pvi->lpszExtensionDesc); } return PyErr_Format(PyExc_AttributeError, "PyVERSION_INFO has no attribute '%s'", name); } int PyVERSION_INFO::setattr(PyObject *self, char *name, PyObject *v) { PyVERSION_INFO *me = (PyVERSION_INFO *)self; if (!me->m_pvi) { PyErr_Format(PyExc_RuntimeError, "VERSION_INFO structure no longer exists"); return -1; } if (v == NULL) { PyErr_SetString(PyExc_AttributeError, "can't delete VERSION_INFO attributes"); return -1; } // @prop string|ExtensionDesc|The description of the extension. else if (strcmp(name, "ExtensionDesc")==0) { if (!PyString_Check(v)) { PyErr_Format(PyExc_ValueError, "FilterDesc must be a string (got %s)", v->ob_type->tp_name); return -1; } if (PyString_Size(v) > HSE_MAX_EXT_DLL_NAME_LEN) { PyErr_Format(PyExc_ValueError, "String is too long - max of %d chars", HSE_MAX_EXT_DLL_NAME_LEN); return -1; } strcpy(me->m_pvi->lpszExtensionDesc, PyString_AsString(v)); } else { PyErr_SetString(PyExc_AttributeError, "can't modify read only VERSION_INFO attributes."); return -1; } return 0; } void PyVERSION_INFO::deallocFunc(PyObject *ob) { delete (PyVERSION_INFO *)ob; } ///////////////////////////////////////////////////////////////////// // Extension block wrapper ///////////////////////////////////////////////////////////////////// #define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0])) #define ECBOFF(e) offsetof(PyECB, e) // @object EXTENSION_CONTROL_BLOCK|A python representation of an ISAPI // EXTENSION_CONTROL_BLOCK. struct memberlist PyECB::PyECB_memberlist[] = { {"Version", T_INT, ECBOFF(m_version), READONLY}, {"ConnID", T_INT, ECBOFF(m_connID), READONLY}, {"Method", T_OBJECT, ECBOFF(m_method), READONLY}, {"QueryString", T_OBJECT, ECBOFF(m_queryString), READONLY}, {"PathInfo", T_OBJECT, ECBOFF(m_pathInfo), READONLY}, {"PathTranslated", T_OBJECT, ECBOFF(m_pathTranslated), READONLY}, {"TotalBytes", T_INT, ECBOFF(m_totalBytes), READONLY}, {"AvailableBytes", T_INT, ECBOFF(m_available), READONLY}, {"AvailableData", T_OBJECT, ECBOFF(m_data), READONLY}, {"ContentType", T_OBJECT, ECBOFF(m_contentType), READONLY}, {"HttpStatusCode", T_INT, ECBOFF(m_HttpStatusCode)}, {"LogData", T_OBJECT, ECBOFF(m_logData)}, {NULL} }; static struct PyMethodDef PyECB_methods[] = { {"write", PyECB::WriteClient, 1}, // @pymeth write|A synonym for WriteClient, this allows you to 'print >> ecb' {"WriteClient", PyECB::WriteClient, 1}, // @pymeth WriteClient| {"GetServerVariable", PyECB::GetServerVariable, 1}, // @pymeth GetServerVariable| {"ReadClient", PyECB::ReadClient, 1}, // @pymeth ReadClient| {"SendResponseHeaders", PyECB::SendResponseHeaders, 1}, // @pymeth SendResponseHeaders| {"DoneWithSession", PyECB::DoneWithSession, 1}, // @pymeth DoneWithSession| {"close", PyECB::DoneWithSession, 1}, // @pymeth close|A synonym for DoneWithSession. {"IsSessionActive", PyECB::IsSessionActive,1}, // @pymeth IsSessionActive|Indicates if DoneWithSession has been called {"Redirect", PyECB::Redirect,1}, // @pymeth Redirect| {"IsKeepAlive", PyECB::IsKeepAlive,1}, // @pymeth IsKeepAlive| {"GetImpersonationToken", PyECB::GetImpersonationToken, 1}, // @pymeth GetImpersonationToken| {NULL} }; PyTypeObject PyECBType = { PyObject_HEAD_INIT(&PyType_Type) 0, "EXTENSION_CONTROL_BLOCK", sizeof(PyECB), 0, PyECB::deallocFunc, /* tp_dealloc */ 0, /* tp_print */ PyECB::getattr, /* tp_getattr */ PyECB::setattr, /* tp_setattr */ 0, 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, 0, /* tp_call */ 0, /* tp_str */ }; PyECB::PyECB(CControlBlock * pcb): m_version(0), // @prop integer|Version|Version info of this spec (read-only) m_connID(0), // @prop integer|ConnID|Context number (read-only) m_method(NULL), // @prop string|Method|REQUEST_METHOD m_queryString(NULL), // @prop string|QueryString|QUERY_STRING m_pathInfo(NULL), // @prop string|PathInfo|PATH_INFO m_pathTranslated(NULL), // @prop string|PathTranslated|PATH_TRANSLATED m_totalBytes(0), // @prop int|TotalBytes|Total bytes indicated from client m_available(0), // @prop int|AvailableBytes|Available number of bytes m_data(NULL), // @prop string|AvailableData|Pointer to cbAvailable bytes m_contentType(NULL), // @prop string|ContentType|Content type of client data m_HttpStatusCode(0), // @prop int|HttpStatusCode|The status of the current transaction when the request is completed. m_logData(NULL), // @prop string|LogData|log data string m_bAsyncDone(false) // async done { ob_type = &PyECBType; _Py_NewReference(this); m_pcb = pcb; EXTENSION_CONTROL_BLOCK * pecb = pcb->GetECB(); m_version = pecb->dwVersion; m_connID = pecb->ConnID; m_HttpStatusCode = pecb->dwHttpStatusCode; m_logData = PyString_FromString(""); m_method = PyString_FromString(pecb->lpszMethod); m_queryString = PyString_FromString(pecb->lpszQueryString); m_pathInfo = PyString_FromString(pecb->lpszPathInfo); m_pathTranslated = PyString_FromString(pecb->lpszPath... [truncated message content] |
From: Mark H. <mha...@us...> - 2004-10-06 05:12:04
|
Update of /cvsroot/pywin32/pywin32/isapi/doc In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17001/isapi/doc Added Files: isapi.html Log Message: Rename pyisapi directory to isapi, so it matches what the user sees, as part of the process of integrating it with the rest of the build. --- NEW FILE: isapi.html --- <!-- NOTE: This HTML is displayed inside the CHM file - hence some hrefs will only work in that environment --> <HTML> <BODY> <TITLE>Introduction to Python ISAPI support</TITLE> <h2>Introduction to Python ISAPI support</h2> <h3>See also</h3> <ul> <li><a href="/isapi_modules.html">The isapi related modules</a> </li> <li><a href="/isapi_objects.html">The isapi related objects</a> </li> </ul> <p><i>Note: if you are viewing this documentation directly from disk, most links in this document will fail - you can also find this document in the CHM file that comes with pywin32, where the links will work</i> <h3>Introduction</h3> This documents Python support for hosting ISAPI exensions and filters inside Microsoft Internet Information Server (IIS). It assumes a basic understanding of the ISAPI filter and extension mechanism. <p> In summary, to implement a filter or extension, you provide a Python module which defines a Filter and/or Extension class. Once your class has been loaded, IIS/ISAPI will, via an extension DLL, call methods on your class. <p> A filter and a class instance need only provide 3 methods - for filters they are called <code>GetFilterVersion</code>, <code>HttpFilterProc</code> and <code>TerminateFilter</code>. For extensions they are named <code>GetExtensionVersion</code>, <code>HttpExtensionProc</code> and <code>TerminateExtension</code>. If you are familiar with writing ISAPI extensions in C/C++, these names and their purpose will be familiar. <p> Most of the work is done in the <code>HttpFilterProc</code> and <code>HttpExtensionProc</code> methods. These both take a single parameter - an <a href="/HTTP_FILTER_CONTEXT.html">HTTP_FILTER_CONTEXT</a> and <a href="/EXTENSION_CONTROL_BLOCK.html">EXTENSION_CONTROL_BLOCK</a> object respectively. <p> In addition to these components, there is an 'isapi' package, containing support facilities (base-classes, exceptions, etc) which can be leveraged by the extension. <h4>Base classes</h4> There are a number of base classes provided to make writing extensions a little simpler. Of particular note is <code>isapi.threaded_extension.ThreadPoolExtension</code>. This implements a thread-pool and informs IIS that the request is progressing in the background. Your sub-class need only provide a <code>Dispatch</code> method, which is called on one of the worker threads rather than the thread that the request came in on. <p> There is base-class for a filter in <code>isapi.simple</code>, but there is no equivilent threaded filter - filters work under a different model, where background processing is not possible. <h4>Samples</h4> Please see the <code>isapi/samples</code> directory for some sample filters and extensions. <H3>Implementation</H3> A Python ISAPI filter extension consists of 2 main components: <UL> <LI>A DLL used by ISAPI to interface with Python.</LI> <LI>A Python script used by that DLL to implement the filter or extension functionality</LI> </UL> <h4>Extension DLL</h4> The DLL is usually managed automatically by the isapi.install module. As the Python script for the extension is installed, a generic DLL provided with the isapi package is installed next to the script, and IIS configured to use this DLL. <p> The name of the DLL always has the same base name as the Python script, but with a leading underscore (_), and an extension of .dll. For example, the sample "redirector.py" will, when installed, have "_redirector.dll" created in the same directory. <p/> The Python script may provide 2 entry points - methods named __FilterFactory__ and __ExtensionFactory__, both taking no arguments and returning a filter or extension object. <h3>Using py2exe and the isapi package</h3> You can instruct py2exe to create a 'frozen' Python ISAPI filter/extension. In this case, py2exe will create a package with everything you need in one directory, and the Python source file embedded in the .zip file. <p> In general, you will want to build a seperate installation executable along with the ISAPI extension. This executable will be built from the same script. See the ISAPI sample in the py2exe distribution. |
From: Mark H. <mha...@us...> - 2004-10-06 05:09:39
|
Update of /cvsroot/pywin32/pywin32/isapi/src In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv16278/src Log Message: Directory /cvsroot/pywin32/pywin32/isapi/src added to the repository |
From: Mark H. <mha...@us...> - 2004-10-06 05:09:38
|
Update of /cvsroot/pywin32/pywin32/isapi/samples In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv16278/samples Log Message: Directory /cvsroot/pywin32/pywin32/isapi/samples added to the repository |
From: Mark H. <mha...@us...> - 2004-10-06 05:09:38
|
Update of /cvsroot/pywin32/pywin32/isapi/test In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv16278/test Log Message: Directory /cvsroot/pywin32/pywin32/isapi/test added to the repository |
From: Mark H. <mha...@us...> - 2004-10-06 05:09:38
|
Update of /cvsroot/pywin32/pywin32/isapi/doc In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv16278/doc Log Message: Directory /cvsroot/pywin32/pywin32/isapi/doc added to the repository |