diff options
Diffstat (limited to 'rubbos/app/httpd-2.0.64/modules/filters')
25 files changed, 7110 insertions, 0 deletions
diff --git a/rubbos/app/httpd-2.0.64/modules/filters/.deps b/rubbos/app/httpd-2.0.64/modules/filters/.deps new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/.deps diff --git a/rubbos/app/httpd-2.0.64/modules/filters/.indent.pro b/rubbos/app/httpd-2.0.64/modules/filters/.indent.pro new file mode 100644 index 00000000..a9fbe9f9 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/.indent.pro @@ -0,0 +1,54 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-Tbuf_area +-Tbuff_struct +-Tbuffy +-Tcmd_how +-Tcmd_parms +-Tcommand_rec +-Tcommand_struct +-Tconn_rec +-Tcore_dir_config +-Tcore_server_config +-Tdir_maker_func +-Tevent +-Tglobals_s +-Thandler_func +-Thandler_rec +-Tjoblist_s +-Tlisten_rec +-Tmerger_func +-Tmode_t +-Tmodule +-Tmodule_struct +-Tmutex +-Tn_long +-Tother_child_rec +-Toverrides_t +-Tparent_score +-Tpid_t +-Tpiped_log +-Tpool +-Trequest_rec +-Trequire_line +-Trlim_t +-Tscoreboard +-Tsemaphore +-Tserver_addr_rec +-Tserver_rec +-Tserver_rec_chain +-Tshort_score +-Ttable +-Ttable_entry +-Tthread +-Tu_wide_int +-Tvtime_t +-Twide_int diff --git a/rubbos/app/httpd-2.0.64/modules/filters/.libs/mod_include.a b/rubbos/app/httpd-2.0.64/modules/filters/.libs/mod_include.a Binary files differnew file mode 100644 index 00000000..02aa56ff --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/.libs/mod_include.a diff --git a/rubbos/app/httpd-2.0.64/modules/filters/.libs/mod_include.la b/rubbos/app/httpd-2.0.64/modules/filters/.libs/mod_include.la new file mode 100644 index 00000000..602915a6 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/.libs/mod_include.la @@ -0,0 +1,35 @@ +# mod_include.la - a libtool library file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='' + +# Names of this library. +library_names='' + +# The name of the static archive. +old_library='mod_include.a' + +# Libraries that this one depends upon. +dependency_libs=' -L/bottlenecks/rubbos/app/httpd-2.0.64/srclib/apr-util/xml/expat/lib' + +# Version information for mod_include. +current= +age= +revision= + +# Is this an already installed library? +installed=no + +# Should we warn about portability when linking against -modules? +shouldnotlink=yes + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='' diff --git a/rubbos/app/httpd-2.0.64/modules/filters/.libs/mod_include.o b/rubbos/app/httpd-2.0.64/modules/filters/.libs/mod_include.o Binary files differnew file mode 100644 index 00000000..d08f85f9 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/.libs/mod_include.o diff --git a/rubbos/app/httpd-2.0.64/modules/filters/Makefile b/rubbos/app/httpd-2.0.64/modules/filters/Makefile new file mode 100644 index 00000000..918a8cbb --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/Makefile @@ -0,0 +1,8 @@ +top_srcdir = /bottlenecks/rubbos/app/httpd-2.0.64 +top_builddir = /bottlenecks/rubbos/app/httpd-2.0.64 +srcdir = /bottlenecks/rubbos/app/httpd-2.0.64/modules/filters +builddir = /bottlenecks/rubbos/app/httpd-2.0.64/modules/filters +VPATH = /bottlenecks/rubbos/app/httpd-2.0.64/modules/filters + +include $(top_srcdir)/build/special.mk + diff --git a/rubbos/app/httpd-2.0.64/modules/filters/Makefile.in b/rubbos/app/httpd-2.0.64/modules/filters/Makefile.in new file mode 100644 index 00000000..167b343d --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/Makefile.in @@ -0,0 +1,3 @@ + +include $(top_srcdir)/build/special.mk + diff --git a/rubbos/app/httpd-2.0.64/modules/filters/NWGNUdeflate b/rubbos/app/httpd-2.0.64/modules/filters/NWGNUdeflate new file mode 100644 index 00000000..4d9cbfa3 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/NWGNUdeflate @@ -0,0 +1,278 @@ +# +# The MOD_DEFLATE module requires the ZLib source which +# can be downloaded from http://www.gzip.org/zlib/ +# +# Declare the sub-directories to be built here +# + +SUBDIRS = \ + $(EOLIST) + +# +# Get the 'head' of the build environment. This includes default targets and +# paths to tools +# + +include $(AP_WORK)\build\NWGNUhead.inc + +# +# build this level's files + +# +# Make sure all needed macro's are defined +# + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = deflate + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Deflate Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Deflate Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)\build\NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = _LibCPrelude + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = _LibCPostlude + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = AUTOUNLOAD, PSEUDOPREEMPTION + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/deflate.nlm \ + $(EOLIST) + +# +# If there is an LIB target, put it here +# +TARGET_lib = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/mod_deflate.o \ + $(OBJDIR)/adler32.o \ + $(OBJDIR)/crc32.o \ + $(OBJDIR)/deflate.o \ + $(OBJDIR)/inflate.o \ + $(OBJDIR)/inffast.o \ + $(OBJDIR)/inftrees.o \ + $(OBJDIR)/trees.o \ + $(OBJDIR)/zutil.o \ + $(EOLIST) + +ifeq "$(wildcard $(ZLIBSDK)/infblock.c)" "$(ZLIBSDK)/infblock.c" +FILES_nlm_objs += \ + $(OBJDIR)/infblock.o \ + $(OBJDIR)/infcodes.o \ + $(OBJDIR)/infutil.o \ + $(EOLIST) +endif + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + libcpre.o \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @$(APR)/aprlib.imp \ + @$(NWOS)/httpd.imp \ + @libc.imp \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + deflate_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)\build\NWGNUhead.inc for examples) +# +install :: nlms FORCE + copy $(OBJDIR)\*.nlm $(INSTALL)\Apache2\modules\*.* + +# +# Any specialized rules here +# + +vpath %.c $(ZLIBSDK) + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(AP_WORK)\build\NWGNUtail.inc + + diff --git a/rubbos/app/httpd-2.0.64/modules/filters/NWGNUextfiltr b/rubbos/app/httpd-2.0.64/modules/filters/NWGNUextfiltr new file mode 100644 index 00000000..8aa5b208 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/NWGNUextfiltr @@ -0,0 +1,248 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)\build\NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = extfiltr + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) External Filter Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = ExtFilter Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)\build\NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = _LibCPrelude + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = _LibCPostlude + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = AUTOUNLOAD, PSEUDOPREEMPTION + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/extfiltr.nlm \ + $(EOLIST) + +# +# If there is an LIB target, put it here +# +TARGET_lib = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/mod_ext_filter.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + libcpre.o \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @$(APR)/aprlib.imp \ + @$(NWOS)/httpd.imp \ + @libc.imp \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + ext_filter_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)\build\NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(AP_WORK)\build\NWGNUtail.inc + + diff --git a/rubbos/app/httpd-2.0.64/modules/filters/NWGNUmakefile b/rubbos/app/httpd-2.0.64/modules/filters/NWGNUmakefile new file mode 100644 index 00000000..c8509428 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/NWGNUmakefile @@ -0,0 +1,255 @@ +# +# Declare the sub-directories to be built here +# + +SUBDIRS = \ + $(EOLIST) + +# +# Get the 'head' of the build environment. This includes default targets and +# paths to tools +# + +include $(AP_WORK)\build\NWGNUhead.inc + +# +# build this level's files + +# +# Make sure all needed macro's are defined +# + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)\build\NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/extfiltr.nlm \ + $(EOLIST) + +# If the zlib libraries source exists then build the mod_deflate module +ifneq "$(ZLIBSDK)" "" +ifeq "$(wildcard $(ZLIBSDK))" "$(ZLIBSDK)" +TARGET_nlm += $(OBJDIR)/deflate.nlm \ + $(EOLIST) +endif +else +TARGET_nlm += $(OBJDIR)/extfiltr.nlm \ + $(EOLIST) +endif + +# +# If there is an LIB target, put it here +# +TARGET_lib = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)\build\NWGNUhead.inc for examples) +# +install :: nlms FORCE + copy $(OBJDIR)\*.nlm $(INSTALL)\Apache2\modules\*.* + +# +# Any specialized rules here +# + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(AP_WORK)\build\NWGNUtail.inc + diff --git a/rubbos/app/httpd-2.0.64/modules/filters/config.m4 b/rubbos/app/httpd-2.0.64/modules/filters/config.m4 new file mode 100644 index 00000000..726dfe29 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/config.m4 @@ -0,0 +1,65 @@ +dnl modules enabled in this directory by default + +dnl APACHE_MODULE(name, helptext[, objects[, structname[, default[, config]]]]) + +APACHE_MODPATH_INIT(filters) + +APACHE_MODULE(ext_filter, external filter module, , , most) +APACHE_MODULE(include, Server Side Includes, , , yes) + +APR_ADDTO(LT_LDFLAGS,-export-dynamic) + +APACHE_MODULE(deflate, Deflate transfer encoding support, , , no, [ + AC_ARG_WITH(z, APACHE_HELP_STRING(--with-z=DIR,use a specific zlib library), + [ + if test "x$withval" != "xyes" && test "x$withval" != "x"; then + ap_zlib_base="$withval" + fi + ]) + if test "x$ap_zlib_base" = "x"; then + AC_MSG_CHECKING([for zlib location]) + AC_CACHE_VAL(ap_cv_zlib,[ + for dir in /usr/local /usr ; do + if test -d $dir && test -f $dir/include/zlib.h; then + ap_cv_zlib=$dir + break + fi + done + ]) + ap_zlib_base=$ap_cv_zlib + if test "x$ap_zlib_base" = "x"; then + enable_deflate=no + AC_MSG_RESULT([not found]) + else + AC_MSG_RESULT([$ap_zlib_base]) + fi + fi + if test "$enable_deflate" != "no"; then + ap_save_includes=$INCLUDE + ap_save_ldflags=$LDFLAGS + ap_save_libs=$LIBS + ap_save_cppflags=$CPPFLAGS + if test "$ap_zlib_base" != "/usr"; then + APR_ADDTO(INCLUDES, [-I${ap_zlib_base}/include]) + dnl put in CPPFLAGS temporarily so that AC_TRY_LINK below will work + CPPFLAGS="$CPPFLAGS $INCLUDES" + APR_ADDTO(LDFLAGS, [-L${ap_zlib_base}/lib]) + if test "x$ap_platform_runtime_link_flag" != "x"; then + APR_ADDTO(LDFLAGS, [$ap_platform_runtime_link_flag${ap_zlib_base}/lib]) + fi + fi + APR_ADDTO(LIBS, [-lz]) + AC_MSG_CHECKING([for zlib library]) + AC_TRY_LINK([#include <zlib.h>], [int i = Z_OK;], + [AC_MSG_RESULT(found) + AC_CHECK_HEADERS(zutil.h)], + [AC_MSG_RESULT(not found) + enable_deflate=no + INCLUDES=$ap_save_includes + LDFLAGS=$ap_save_ldflags + LIBS=$ap_save_libs]) + CPPFLAGS=$ap_save_cppflags + fi +]) + +APACHE_MODPATH_FINISH diff --git a/rubbos/app/httpd-2.0.64/modules/filters/mod_deflate.c b/rubbos/app/httpd-2.0.64/modules/filters/mod_deflate.c new file mode 100644 index 00000000..610be52d --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/mod_deflate.c @@ -0,0 +1,875 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Portions of this software are based upon public domain software + * (zlib functions gz_open and gzwrite) + */ + +/* + * mod_deflate.c: Perform deflate transfer-encoding on the fly + * + * Written by Ian Holsman + * + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_log.h" +#include "apr_strings.h" +#include "apr_general.h" +#include "util_filter.h" +#include "apr_buckets.h" +#include "http_request.h" +#define APR_WANT_STRFUNC +#include "apr_want.h" + +#include "zlib.h" + +#ifdef HAVE_ZUTIL_H +#include "zutil.h" +#else +/* As part of the encoding process, we must send what our OS_CODE is + * (or so it seems based on what I can tell of how gzip encoding works). + * + * zutil.h is not always included with zlib distributions (it is a private + * header), so this is straight from zlib 1.1.3's zutil.h. + */ +#ifdef OS2 +#define OS_CODE 0x06 +#endif + +#ifdef WIN32 /* Window 95 & Windows NT */ +#define OS_CODE 0x0b +#endif + +#if defined(VAXC) || defined(VMS) +#define OS_CODE 0x02 +#endif + +#ifdef AMIGA +#define OS_CODE 0x01 +#endif + +#if defined(ATARI) || defined(atarist) +#define OS_CODE 0x05 +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +#define OS_CODE 0x07 +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +#define OS_CODE 0x0F +#endif + +#ifdef TOPS20 +#define OS_CODE 0x0a +#endif + +#ifndef OS_CODE +#define OS_CODE 0x03 /* assume Unix */ +#endif +#endif + +static const char deflateFilterName[] = "DEFLATE"; +module AP_MODULE_DECLARE_DATA deflate_module; + +typedef struct deflate_filter_config_t +{ + int windowSize; + int memlevel; + int compressionlevel; + apr_size_t bufferSize; + char *note_ratio_name; + char *note_input_name; + char *note_output_name; +} deflate_filter_config; + +/* windowsize is negative to suppress Zlib header */ +#define DEFAULT_COMPRESSION Z_DEFAULT_COMPRESSION +#define DEFAULT_WINDOWSIZE -15 +#define DEFAULT_MEMLEVEL 9 +#define DEFAULT_BUFFERSIZE 8096 + +/* Outputs a long in LSB order to the given file + * only the bottom 4 bits are required for the deflate file format. + */ +static void putLong(unsigned char *string, unsigned long x) +{ + string[0] = (unsigned char)(x & 0xff); + string[1] = (unsigned char)((x & 0xff00) >> 8); + string[2] = (unsigned char)((x & 0xff0000) >> 16); + string[3] = (unsigned char)((x & 0xff000000) >> 24); +} + +/* Inputs a string and returns a long. + */ +static unsigned long getLong(unsigned char *string) +{ + return ((unsigned long)string[0]) + | (((unsigned long)string[1]) << 8) + | (((unsigned long)string[2]) << 16) + | (((unsigned long)string[3]) << 24); +} + +static void *create_deflate_server_config(apr_pool_t *p, server_rec *s) +{ + deflate_filter_config *c = apr_pcalloc(p, sizeof *c); + + c->memlevel = DEFAULT_MEMLEVEL; + c->windowSize = DEFAULT_WINDOWSIZE; + c->bufferSize = DEFAULT_BUFFERSIZE; + c->compressionlevel = DEFAULT_COMPRESSION; + + return c; +} + +static const char *deflate_set_window_size(cmd_parms *cmd, void *dummy, + const char *arg) +{ + deflate_filter_config *c = ap_get_module_config(cmd->server->module_config, + &deflate_module); + int i; + + i = atoi(arg); + + if (i < 1 || i > 15) + return "DeflateWindowSize must be between 1 and 15"; + + c->windowSize = i * -1; + + return NULL; +} + +static const char *deflate_set_buffer_size(cmd_parms *cmd, void *dummy, + const char *arg) +{ + deflate_filter_config *c = ap_get_module_config(cmd->server->module_config, + &deflate_module); + int n = atoi(arg); + + if (n <= 0) { + return "DeflateBufferSize should be positive"; + } + + c->bufferSize = (apr_size_t)n; + + return NULL; +} +static const char *deflate_set_note(cmd_parms *cmd, void *dummy, + const char *arg1, const char *arg2) +{ + deflate_filter_config *c = ap_get_module_config(cmd->server->module_config, + &deflate_module); + + if (arg2 == NULL) { + c->note_ratio_name = apr_pstrdup(cmd->pool, arg1); + } + else if (!strcasecmp(arg1, "ratio")) { + c->note_ratio_name = apr_pstrdup(cmd->pool, arg2); + } + else if (!strcasecmp(arg1, "input")) { + c->note_input_name = apr_pstrdup(cmd->pool, arg2); + } + else if (!strcasecmp(arg1, "output")) { + c->note_output_name = apr_pstrdup(cmd->pool, arg2); + } + else { + return apr_psprintf(cmd->pool, "Unknown note type %s", arg1); + } + + return NULL; +} + +static const char *deflate_set_memlevel(cmd_parms *cmd, void *dummy, + const char *arg) +{ + deflate_filter_config *c = ap_get_module_config(cmd->server->module_config, + &deflate_module); + int i; + + i = atoi(arg); + + if (i < 1 || i > 9) + return "DeflateMemLevel must be between 1 and 9"; + + c->memlevel = i; + + return NULL; +} + +static const char *deflate_set_compressionlevel(cmd_parms *cmd, void *dummy, + const char *arg) +{ + deflate_filter_config *c = ap_get_module_config(cmd->server->module_config, + &deflate_module); + int i; + + i = atoi(arg); + + if (i < 1 || i > 9) + return "Compression Level must be between 1 and 9"; + + c->compressionlevel = i; + + return NULL; +} + +/* magic header */ +static char deflate_magic[2] = { '\037', '\213' }; + +typedef struct deflate_ctx_t +{ + z_stream stream; + unsigned char *buffer; + unsigned long crc; + apr_bucket_brigade *bb, *proc_bb; +} deflate_ctx; + +static apr_status_t deflate_out_filter(ap_filter_t *f, + apr_bucket_brigade *bb) +{ + apr_bucket *e; + request_rec *r = f->r; + deflate_ctx *ctx = f->ctx; + int zRC; + deflate_filter_config *c = ap_get_module_config(r->server->module_config, + &deflate_module); + + /* If we don't have a context, we need to ensure that it is okay to send + * the deflated content. If we have a context, that means we've done + * this before and we liked it. + * This could be not so nice if we always fail. But, if we succeed, + * we're in better shape. + */ + if (!ctx) { + char *buf, *token; + const char *encoding, *accepts; + + /* only work on main request/no subrequests */ + if (r->main) { + ap_remove_output_filter(f); + return ap_pass_brigade(f->next, bb); + } + + /* some browsers might have problems, so set no-gzip + * (with browsermatch) for them + */ + if (apr_table_get(r->subprocess_env, "no-gzip")) { + ap_remove_output_filter(f); + return ap_pass_brigade(f->next, bb); + } + + /* Some browsers might have problems with content types + * other than text/html, so set gzip-only-text/html + * (with browsermatch) for them + */ + if (r->content_type == NULL + || strncmp(r->content_type, "text/html", 9)) { + const char *env_value = apr_table_get(r->subprocess_env, + "gzip-only-text/html"); + if ( env_value && (strcmp(env_value,"1") == 0) ) { + ap_remove_output_filter(f); + return ap_pass_brigade(f->next, bb); + } + } + + /* Let's see what our current Content-Encoding is. + * If it's already encoded, don't compress again. + * (We could, but let's not.) + */ + encoding = apr_table_get(r->headers_out, "Content-Encoding"); + if (encoding) { + const char *err_enc; + + err_enc = apr_table_get(r->err_headers_out, "Content-Encoding"); + if (err_enc) { + encoding = apr_pstrcat(r->pool, encoding, ",", err_enc, NULL); + } + } + else { + encoding = apr_table_get(r->err_headers_out, "Content-Encoding"); + } + + if (r->content_encoding) { + encoding = encoding ? apr_pstrcat(r->pool, encoding, ",", + r->content_encoding, NULL) + : r->content_encoding; + } + + if (encoding) { + const char *tmp = encoding; + + token = ap_get_token(r->pool, &tmp, 0); + while (token && *token) { + /* stolen from mod_negotiation: */ + if (strcmp(token, "identity") && strcmp(token, "7bit") && + strcmp(token, "8bit") && strcmp(token, "binary")) { + + ap_remove_output_filter(f); + return ap_pass_brigade(f->next, bb); + } + + /* Otherwise, skip token */ + if (*tmp) { + ++tmp; + } + token = (*tmp) ? ap_get_token(r->pool, &tmp, 0) : NULL; + } + } + + /* Even if we don't accept this request based on it not having + * the Accept-Encoding, we need to note that we were looking + * for this header and downstream proxies should be aware of that. + */ + apr_table_mergen(r->headers_out, "Vary", "Accept-Encoding"); + + /* if they don't have the line, then they can't play */ + accepts = apr_table_get(r->headers_in, "Accept-Encoding"); + if (accepts == NULL) { + ap_remove_output_filter(f); + return ap_pass_brigade(f->next, bb); + } + + token = ap_get_token(r->pool, &accepts, 0); + while (token && token[0] && strcasecmp(token, "gzip")) { + /* skip parameters, XXX: ;q=foo evaluation? */ + while (*accepts == ';') { + ++accepts; + token = ap_get_token(r->pool, &accepts, 1); + } + + /* retrieve next token */ + if (*accepts == ',') { + ++accepts; + } + token = (*accepts) ? ap_get_token(r->pool, &accepts, 0) : NULL; + } + + /* No acceptable token found. */ + if (token == NULL || token[0] == '\0') { + ap_remove_output_filter(f); + return ap_pass_brigade(f->next, bb); + } + + /* We're cool with filtering this. */ + ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx)); + ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc); + ctx->buffer = apr_palloc(r->pool, c->bufferSize); + + zRC = deflateInit2(&ctx->stream, c->compressionlevel, Z_DEFLATED, + c->windowSize, c->memlevel, + Z_DEFAULT_STRATEGY); + + if (zRC != Z_OK) { + f->ctx = NULL; + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "unable to init Zlib: " + "deflateInit2 returned %d: URL %s", + zRC, r->uri); + return ap_pass_brigade(f->next, bb); + } + + /* RFC 1952 Section 2.3 dictates the gzip header: + * + * +---+---+---+---+---+---+---+---+---+---+ + * |ID1|ID2|CM |FLG| MTIME |XFL|OS | + * +---+---+---+---+---+---+---+---+---+---+ + * + * If we wish to populate in MTIME (as hinted in RFC 1952), do: + * putLong(date_array, apr_time_now() / APR_USEC_PER_SEC); + * where date_array is a char[4] and then print date_array in the + * MTIME position. WARNING: ENDIANNESS ISSUE HERE. + */ + buf = apr_psprintf(r->pool, "%c%c%c%c%c%c%c%c%c%c", deflate_magic[0], + deflate_magic[1], Z_DEFLATED, 0 /* flags */, + 0, 0, 0, 0 /* 4 chars for mtime */, + 0 /* xflags */, OS_CODE); + e = apr_bucket_pool_create(buf, 10, r->pool, f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(ctx->bb, e); + + /* If the entire Content-Encoding is "identity", we can replace it. */ + if (!encoding || !strcasecmp(encoding, "identity")) { + apr_table_setn(r->headers_out, "Content-Encoding", "gzip"); + } + else { + apr_table_mergen(r->headers_out, "Content-Encoding", "gzip"); + } + apr_table_unset(r->headers_out, "Content-Length"); + + /* initialize deflate output buffer */ + ctx->stream.next_out = ctx->buffer; + ctx->stream.avail_out = c->bufferSize; + } + + while (!APR_BRIGADE_EMPTY(bb)) + { + const char *data; + apr_bucket *b; + apr_size_t len; + int done = 0; + + e = APR_BRIGADE_FIRST(bb); + + if (APR_BUCKET_IS_EOS(e)) { + char *buf; + unsigned int deflate_len; + + ctx->stream.avail_in = 0; /* should be zero already anyway */ + for (;;) { + deflate_len = c->bufferSize - ctx->stream.avail_out; + + if (deflate_len != 0) { + b = apr_bucket_heap_create((char *)ctx->buffer, + deflate_len, NULL, + f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(ctx->bb, b); + ctx->stream.next_out = ctx->buffer; + ctx->stream.avail_out = c->bufferSize; + } + + if (done) { + break; + } + + zRC = deflate(&ctx->stream, Z_FINISH); + + if (deflate_len == 0 && zRC == Z_BUF_ERROR) { + zRC = Z_OK; + } + + done = (ctx->stream.avail_out != 0 || zRC == Z_STREAM_END); + + if (zRC != Z_OK && zRC != Z_STREAM_END) { + break; + } + } + + buf = apr_palloc(r->pool, 8); + putLong((unsigned char *)&buf[0], ctx->crc); + putLong((unsigned char *)&buf[4], ctx->stream.total_in); + + b = apr_bucket_pool_create(buf, 8, r->pool, f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(ctx->bb, b); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + "Zlib: Compressed %ld to %ld : URL %s", + ctx->stream.total_in, ctx->stream.total_out, r->uri); + + /* leave notes for logging */ + if (c->note_input_name) { + apr_table_setn(r->notes, c->note_input_name, + (ctx->stream.total_in > 0) + ? apr_off_t_toa(r->pool, + ctx->stream.total_in) + : "-"); + } + + if (c->note_output_name) { + apr_table_setn(r->notes, c->note_output_name, + (ctx->stream.total_in > 0) + ? apr_off_t_toa(r->pool, + ctx->stream.total_out) + : "-"); + } + + if (c->note_ratio_name) { + apr_table_setn(r->notes, c->note_ratio_name, + (ctx->stream.total_in > 0) + ? apr_itoa(r->pool, + (int)(ctx->stream.total_out + * 100 + / ctx->stream.total_in)) + : "-"); + } + + deflateEnd(&ctx->stream); + + /* Remove EOS from the old list, and insert into the new. */ + APR_BUCKET_REMOVE(e); + APR_BRIGADE_INSERT_TAIL(ctx->bb, e); + + /* Okay, we've seen the EOS. + * Time to pass it along down the chain. + */ + return ap_pass_brigade(f->next, ctx->bb); + } + + if (APR_BUCKET_IS_FLUSH(e)) { + apr_bucket *bkt; + apr_status_t rv; + + apr_bucket_delete(e); + + if (ctx->stream.avail_in > 0) { + zRC = deflate(&(ctx->stream), Z_SYNC_FLUSH); + if (zRC != Z_OK) { + return APR_EGENERAL; + } + } + + ctx->stream.next_out = ctx->buffer; + len = c->bufferSize - ctx->stream.avail_out; + + b = apr_bucket_heap_create((char *)ctx->buffer, len, + NULL, f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(ctx->bb, b); + ctx->stream.avail_out = c->bufferSize; + + bkt = apr_bucket_flush_create(f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(ctx->bb, bkt); + rv = ap_pass_brigade(f->next, ctx->bb); + if (rv != APR_SUCCESS) { + return rv; + } + continue; + } + + /* read */ + apr_bucket_read(e, &data, &len, APR_BLOCK_READ); + + /* This crc32 function is from zlib. */ + ctx->crc = crc32(ctx->crc, (const Bytef *)data, len); + + /* write */ + ctx->stream.next_in = (unsigned char *)data; /* We just lost const-ness, + * but we'll just have to + * trust zlib */ + ctx->stream.avail_in = len; + + while (ctx->stream.avail_in != 0) { + if (ctx->stream.avail_out == 0) { + apr_status_t rv; + + ctx->stream.next_out = ctx->buffer; + len = c->bufferSize - ctx->stream.avail_out; + + b = apr_bucket_heap_create((char *)ctx->buffer, len, + NULL, f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(ctx->bb, b); + ctx->stream.avail_out = c->bufferSize; + /* Send what we have right now to the next filter. */ + rv = ap_pass_brigade(f->next, ctx->bb); + if (rv != APR_SUCCESS) { + return rv; + } + } + + zRC = deflate(&(ctx->stream), Z_NO_FLUSH); + + if (zRC != Z_OK) + return APR_EGENERAL; + } + + apr_bucket_delete(e); + } + + apr_brigade_cleanup(bb); + return APR_SUCCESS; +} + +/* This is the deflate input filter (inflates). */ +static apr_status_t deflate_in_filter(ap_filter_t *f, + apr_bucket_brigade *bb, + ap_input_mode_t mode, + apr_read_type_e block, + apr_off_t readbytes) +{ + apr_bucket *bkt; + request_rec *r = f->r; + deflate_ctx *ctx = f->ctx; + int zRC; + apr_status_t rv; + deflate_filter_config *c; + + /* just get out of the way of things we don't want. */ + if (mode != AP_MODE_READBYTES) { + return ap_get_brigade(f->next, bb, mode, block, readbytes); + } + + c = ap_get_module_config(r->server->module_config, &deflate_module); + + if (!ctx) { + int found = 0; + char *token, deflate_hdr[10]; + const char *encoding; + apr_size_t len; + + /* only work on main request/no subrequests */ + if (r->main) { + ap_remove_input_filter(f); + return ap_get_brigade(f->next, bb, mode, block, readbytes); + } + + /* Let's see what our current Content-Encoding is. + * If gzip is present, don't gzip again. (We could, but let's not.) + */ + encoding = apr_table_get(r->headers_in, "Content-Encoding"); + if (encoding) { + const char *tmp = encoding; + + token = ap_get_token(r->pool, &tmp, 0); + while (token && token[0]) { + if (!strcasecmp(token, "gzip")) { + found = 1; + break; + } + /* Otherwise, skip token */ + tmp++; + token = ap_get_token(r->pool, &tmp, 0); + } + } + + if (found == 0) { + ap_remove_input_filter(f); + return ap_get_brigade(f->next, bb, mode, block, readbytes); + } + + f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx)); + ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc); + ctx->proc_bb = apr_brigade_create(r->pool, f->c->bucket_alloc); + ctx->buffer = apr_palloc(r->pool, c->bufferSize); + + rv = ap_get_brigade(f->next, ctx->bb, AP_MODE_READBYTES, block, 10); + if (rv != APR_SUCCESS) { + return rv; + } + + len = 10; + rv = apr_brigade_flatten(ctx->bb, deflate_hdr, &len); + if (rv != APR_SUCCESS) { + return rv; + } + + /* We didn't get the magic bytes. */ + if (len != 10 || + deflate_hdr[0] != deflate_magic[0] || + deflate_hdr[1] != deflate_magic[1]) { + return APR_EGENERAL; + } + + /* We can't handle flags for now. */ + if (deflate_hdr[3] != 0) { + return APR_EGENERAL; + } + + zRC = inflateInit2(&ctx->stream, c->windowSize); + + if (zRC != Z_OK) { + f->ctx = NULL; + inflateEnd(&ctx->stream); + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "unable to init Zlib: " + "inflateInit2 returned %d: URL %s", + zRC, r->uri); + ap_remove_input_filter(f); + return ap_get_brigade(f->next, bb, mode, block, readbytes); + } + + /* initialize deflate output buffer */ + ctx->stream.next_out = ctx->buffer; + ctx->stream.avail_out = c->bufferSize; + + apr_brigade_cleanup(ctx->bb); + } + + if (APR_BRIGADE_EMPTY(ctx->proc_bb)) { + rv = ap_get_brigade(f->next, ctx->bb, mode, block, readbytes); + + if (rv != APR_SUCCESS) { + /* What about APR_EAGAIN errors? */ + inflateEnd(&ctx->stream); + return rv; + } + + APR_BRIGADE_FOREACH(bkt, ctx->bb) { + const char *data; + apr_size_t len; + + /* If we actually see the EOS, that means we screwed up! */ + if (APR_BUCKET_IS_EOS(bkt)) { + inflateEnd(&ctx->stream); + return APR_EGENERAL; + } + + if (APR_BUCKET_IS_FLUSH(bkt)) { + apr_bucket *tmp_heap; + zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH); + if (zRC != Z_OK) { + inflateEnd(&ctx->stream); + return APR_EGENERAL; + } + + ctx->stream.next_out = ctx->buffer; + len = c->bufferSize - ctx->stream.avail_out; + + ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); + tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len, + NULL, f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap); + ctx->stream.avail_out = c->bufferSize; + + /* Move everything to the returning brigade. */ + APR_BUCKET_REMOVE(bkt); + APR_BRIGADE_CONCAT(bb, ctx->bb); + break; + } + + /* read */ + apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ); + + /* pass through zlib inflate. */ + ctx->stream.next_in = (unsigned char *)data; + ctx->stream.avail_in = len; + + zRC = Z_OK; + + while (ctx->stream.avail_in != 0) { + if (ctx->stream.avail_out == 0) { + apr_bucket *tmp_heap; + ctx->stream.next_out = ctx->buffer; + len = c->bufferSize - ctx->stream.avail_out; + + ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); + tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len, + NULL, f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap); + ctx->stream.avail_out = c->bufferSize; + } + + zRC = inflate(&ctx->stream, Z_NO_FLUSH); + + if (zRC == Z_STREAM_END) { + break; + } + + if (zRC != Z_OK) { + inflateEnd(&ctx->stream); + return APR_EGENERAL; + } + } + if (zRC == Z_STREAM_END) { + apr_bucket *tmp_heap, *eos; + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + "Zlib: Inflated %ld to %ld : URL %s", + ctx->stream.total_in, ctx->stream.total_out, + r->uri); + + len = c->bufferSize - ctx->stream.avail_out; + + ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); + tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len, + NULL, f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap); + ctx->stream.avail_out = c->bufferSize; + + /* Is the remaining 8 bytes already in the avail stream? */ + if (ctx->stream.avail_in >= 8) { + unsigned long compCRC, compLen; + compCRC = getLong(ctx->stream.next_in); + if (ctx->crc != compCRC) { + inflateEnd(&ctx->stream); + return APR_EGENERAL; + } + ctx->stream.next_in += 4; + compLen = getLong(ctx->stream.next_in); + if (ctx->stream.total_out != compLen) { + inflateEnd(&ctx->stream); + return APR_EGENERAL; + } + } + else { + /* FIXME: We need to grab the 8 verification bytes + * from the wire! */ + inflateEnd(&ctx->stream); + return APR_EGENERAL; + } + + inflateEnd(&ctx->stream); + + eos = apr_bucket_eos_create(f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, eos); + break; + } + + } + apr_brigade_cleanup(ctx->bb); + } + + /* If we are about to return nothing for a 'blocking' read and we have + * some data in our zlib buffer, flush it out so we can return something. + */ + if (block == APR_BLOCK_READ && + APR_BRIGADE_EMPTY(ctx->proc_bb) && + ctx->stream.avail_out < c->bufferSize) { + apr_bucket *tmp_heap; + apr_size_t len; + ctx->stream.next_out = ctx->buffer; + len = c->bufferSize - ctx->stream.avail_out; + + ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); + tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len, + NULL, f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap); + ctx->stream.avail_out = c->bufferSize; + } + + if (!APR_BRIGADE_EMPTY(ctx->proc_bb)) { + apr_bucket_brigade *newbb; + + /* May return APR_INCOMPLETE which is fine by us. */ + apr_brigade_partition(ctx->proc_bb, readbytes, &bkt); + + newbb = apr_brigade_split(ctx->proc_bb, bkt); + APR_BRIGADE_CONCAT(bb, ctx->proc_bb); + APR_BRIGADE_CONCAT(ctx->proc_bb, newbb); + } + + return APR_SUCCESS; +} + +static void register_hooks(apr_pool_t *p) +{ + ap_register_output_filter(deflateFilterName, deflate_out_filter, NULL, + AP_FTYPE_CONTENT_SET); + ap_register_input_filter(deflateFilterName, deflate_in_filter, NULL, + AP_FTYPE_CONTENT_SET); +} + +static const command_rec deflate_filter_cmds[] = { + AP_INIT_TAKE12("DeflateFilterNote", deflate_set_note, NULL, RSRC_CONF, + "Set a note to report on compression ratio"), + AP_INIT_TAKE1("DeflateWindowSize", deflate_set_window_size, NULL, + RSRC_CONF, "Set the Deflate window size (1-15)"), + AP_INIT_TAKE1("DeflateBufferSize", deflate_set_buffer_size, NULL, RSRC_CONF, + "Set the Deflate Buffer Size"), + AP_INIT_TAKE1("DeflateMemLevel", deflate_set_memlevel, NULL, RSRC_CONF, + "Set the Deflate Memory Level (1-9)"), + AP_INIT_TAKE1("DeflateCompressionLevel", deflate_set_compressionlevel, NULL, RSRC_CONF, + "Set the Deflate Compression Level (1-9)"), + {NULL} +}; + +module AP_MODULE_DECLARE_DATA deflate_module = { + STANDARD20_MODULE_STUFF, + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + create_deflate_server_config, /* server config */ + NULL, /* merge server config */ + deflate_filter_cmds, /* command table */ + register_hooks /* register hooks */ +}; diff --git a/rubbos/app/httpd-2.0.64/modules/filters/mod_deflate.dsp b/rubbos/app/httpd-2.0.64/modules/filters/mod_deflate.dsp new file mode 100644 index 00000000..e26600ca --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/mod_deflate.dsp @@ -0,0 +1,127 @@ +# Microsoft Developer Studio Project File - Name="mod_deflate" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_deflate - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_deflate.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_deflate.mak" CFG="mod_deflate - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_deflate - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_deflate - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_deflate - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "HAVE_ZUTIL_H" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/zlib" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_deflate_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:".\Release\mod_deflate.so" /base:@..\..\os\win32\BaseAddr.ref,mod_deflate.so +# ADD LINK32 kernel32.lib zdll.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_deflate.so" /libpath:"../../srclib/zlib" /base:@..\..\os\win32\BaseAddr.ref,mod_deflate.so /opt:ref + +!ELSEIF "$(CFG)" == "mod_deflate - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/zlib" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "HAVE_ZUTIL_H" /Fd"Debug\mod_deflate_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_deflate.so" /base:@..\..\os\win32\BaseAddr.ref,mod_deflate.so +# ADD LINK32 kernel32.lib zdll.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_deflate.so" /libpath:"../../srclib/zlib" /base:@..\..\os\win32\BaseAddr.ref,mod_deflate.so + +!ENDIF + +# Begin Target + +# Name "mod_deflate - Win32 Release" +# Name "mod_deflate - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_deflate.c +# End Source File +# Begin Source File + +SOURCE=.\mod_deflate.rc +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\win32ver.awk + +!IF "$(CFG)" == "mod_deflate - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_deflate.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_deflate.so "deflate_module for Apache" ../../include/ap_release.h > .\mod_deflate.rc + +# End Custom Build + +!ELSEIF "$(CFG)" == "mod_deflate - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_deflate.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_deflate.so "deflate_module for Apache" ../../include/ap_release.h > .\mod_deflate.rc + +# End Custom Build + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/rubbos/app/httpd-2.0.64/modules/filters/mod_deflate.exp b/rubbos/app/httpd-2.0.64/modules/filters/mod_deflate.exp new file mode 100644 index 00000000..9ec76883 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/mod_deflate.exp @@ -0,0 +1 @@ +deflate_module diff --git a/rubbos/app/httpd-2.0.64/modules/filters/mod_ext_filter.c b/rubbos/app/httpd-2.0.64/modules/filters/mod_ext_filter.c new file mode 100644 index 00000000..cc77b40a --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/mod_ext_filter.c @@ -0,0 +1,890 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * mod_ext_filter allows Unix-style filters to filter http content. + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_log.h" +#include "http_protocol.h" +#define CORE_PRIVATE +#include "http_core.h" +#include "apr_buckets.h" +#include "util_filter.h" +#include "util_script.h" +#include "util_time.h" +#include "apr_strings.h" +#include "apr_hash.h" +#include "apr_lib.h" +#include "apr_poll.h" +#define APR_WANT_STRFUNC +#include "apr_want.h" + +typedef struct ef_server_t { + apr_pool_t *p; + apr_hash_t *h; +} ef_server_t; + +typedef struct ef_filter_t { + const char *name; + enum {INPUT_FILTER=1, OUTPUT_FILTER} mode; + ap_filter_type ftype; + const char *command; + const char *enable_env; + const char *disable_env; + char **args; + const char *intype; /* list of IMTs we process (well, just one for now) */ +#define INTYPE_ALL (char *)1 + const char *outtype; /* IMT of filtered output */ +#define OUTTYPE_UNCHANGED (char *)1 + int preserves_content_length; +} ef_filter_t; + +typedef struct ef_dir_t { + int debug; + int log_stderr; +} ef_dir_t; + +typedef struct ef_ctx_t { + apr_pool_t *p; + apr_proc_t *proc; + apr_procattr_t *procattr; + ef_dir_t *dc; + ef_filter_t *filter; + int noop; +#if APR_FILES_AS_SOCKETS + apr_pollfd_t *pollset; +#endif +} ef_ctx_t; + +module AP_MODULE_DECLARE_DATA ext_filter_module; +static const server_rec *main_server; + +static apr_status_t ef_output_filter(ap_filter_t *, apr_bucket_brigade *); + +#define DBGLVL_SHOWOPTIONS 1 +#define DBGLVL_ERRORCHECK 2 +#define DBGLVL_GORY 9 + +#define ERRFN_USERDATA_KEY "EXTFILTCHILDERRFN" + +static void *create_ef_dir_conf(apr_pool_t *p, char *dummy) +{ + ef_dir_t *dc = (ef_dir_t *)apr_pcalloc(p, sizeof(ef_dir_t)); + + dc->debug = -1; + dc->log_stderr = -1; + + return dc; +} + +static void *create_ef_server_conf(apr_pool_t *p, server_rec *s) +{ + ef_server_t *conf; + + conf = (ef_server_t *)apr_pcalloc(p, sizeof(ef_server_t)); + conf->p = p; + conf->h = apr_hash_make(conf->p); + return conf; +} + +static void *merge_ef_dir_conf(apr_pool_t *p, void *basev, void *overridesv) +{ + ef_dir_t *a = (ef_dir_t *)apr_pcalloc (p, sizeof(ef_dir_t)); + ef_dir_t *base = (ef_dir_t *)basev, *over = (ef_dir_t *)overridesv; + + if (over->debug != -1) { /* if admin coded something... */ + a->debug = over->debug; + } + else { + a->debug = base->debug; + } + + if (over->log_stderr != -1) { /* if admin coded something... */ + a->log_stderr = over->log_stderr; + } + else { + a->log_stderr = base->log_stderr; + } + + return a; +} + +static const char *add_options(cmd_parms *cmd, void *in_dc, + const char *arg) +{ + ef_dir_t *dc = in_dc; + + if (!strncasecmp(arg, "DebugLevel=", 11)) { + dc->debug = atoi(arg + 11); + } + else if (!strcasecmp(arg, "LogStderr")) { + dc->log_stderr = 1; + } + else if (!strcasecmp(arg, "NoLogStderr")) { + dc->log_stderr = 0; + } + else { + return apr_pstrcat(cmd->temp_pool, + "Invalid ExtFilterOptions option: ", + arg, + NULL); + } + + return NULL; +} + +static const char *parse_cmd(apr_pool_t *p, const char **args, ef_filter_t *filter) +{ + if (**args == '"') { + const char *start = *args + 1; + char *parms; + int escaping = 0; + apr_status_t rv; + + ++*args; /* move past leading " */ + /* find true end of args string (accounting for escaped quotes) */ + while (**args && (**args != '"' || (**args == '"' && escaping))) { + if (escaping) { + escaping = 0; + } + else if (**args == '\\') { + escaping = 1; + } + ++*args; + } + if (**args != '"') { + return "Expected cmd= delimiter"; + } + /* copy *just* the arg string for parsing, */ + parms = apr_pstrndup(p, start, *args - start); + ++*args; /* move past trailing " */ + + /* parse and tokenize the args. */ + rv = apr_tokenize_to_argv(parms, &(filter->args), p); + if (rv != APR_SUCCESS) { + return "cmd= parse error"; + } + } + else + { + /* simple path */ + /* Allocate space for two argv pointers and parse the args. */ + filter->args = (char **)apr_palloc(p, 2 * sizeof(char *)); + filter->args[0] = ap_getword_white(p, args); + filter->args[1] = NULL; /* end of args */ + } + if (!filter->args[0]) { + return "Invalid cmd= parameter"; + } + filter->command = filter->args[0]; + + return NULL; +} + +static const char *define_filter(cmd_parms *cmd, void *dummy, const char *args) +{ + ef_server_t *conf = ap_get_module_config(cmd->server->module_config, + &ext_filter_module); + const char *token; + const char *name; + ef_filter_t *filter; + + name = ap_getword_white(cmd->pool, &args); + if (!name) { + return "Filter name not found"; + } + + if (apr_hash_get(conf->h, name, APR_HASH_KEY_STRING)) { + return apr_psprintf(cmd->pool, "ExtFilter %s is already defined", + name); + } + + filter = (ef_filter_t *)apr_pcalloc(conf->p, sizeof(ef_filter_t)); + filter->name = name; + filter->mode = OUTPUT_FILTER; + filter->ftype = AP_FTYPE_RESOURCE; + apr_hash_set(conf->h, name, APR_HASH_KEY_STRING, filter); + + while (*args) { + while (apr_isspace(*args)) { + ++args; + } + + /* Nasty parsing... I wish I could simply use ap_getword_white() + * here and then look at the token, but ap_getword_white() doesn't + * do the right thing when we have cmd="word word word" + */ + if (!strncasecmp(args, "preservescontentlength", 22)) { + token = ap_getword_white(cmd->pool, &args); + if (!strcasecmp(token, "preservescontentlength")) { + filter->preserves_content_length = 1; + } + else { + return apr_psprintf(cmd->pool, + "mangled argument `%s'", + token); + } + continue; + } + + if (!strncasecmp(args, "mode=", 5)) { + args += 5; + token = ap_getword_white(cmd->pool, &args); + if (!strcasecmp(token, "output")) { + filter->mode = OUTPUT_FILTER; + } + else if (!strcasecmp(token, "input")) { + filter->mode = INPUT_FILTER; + } + else { + return apr_psprintf(cmd->pool, "Invalid mode: `%s'", + token); + } + continue; + } + + if (!strncasecmp(args, "ftype=", 6)) { + args += 6; + token = ap_getword_white(cmd->pool, &args); + filter->ftype = atoi(token); + continue; + } + + if (!strncasecmp(args, "enableenv=", 10)) { + args += 10; + token = ap_getword_white(cmd->pool, &args); + filter->enable_env = token; + continue; + } + + if (!strncasecmp(args, "disableenv=", 11)) { + args += 11; + token = ap_getword_white(cmd->pool, &args); + filter->disable_env = token; + continue; + } + + if (!strncasecmp(args, "intype=", 7)) { + args += 7; + filter->intype = ap_getword_white(cmd->pool, &args); + continue; + } + + if (!strncasecmp(args, "outtype=", 8)) { + args += 8; + filter->outtype = ap_getword_white(cmd->pool, &args); + continue; + } + + if (!strncasecmp(args, "cmd=", 4)) { + args += 4; + if ((token = parse_cmd(cmd->pool, &args, filter))) { + return token; + } + continue; + } + + return apr_psprintf(cmd->pool, "Unexpected parameter: `%s'", + args); + } + + /* parsing is done... register the filter + */ + if (filter->mode == OUTPUT_FILTER) { + /* XXX need a way to ensure uniqueness among all filters */ + ap_register_output_filter(filter->name, ef_output_filter, NULL, filter->ftype); + } +#if 0 /* no input filters yet */ + else if (filter->mode == INPUT_FILTER) { + /* XXX need a way to ensure uniqueness among all filters */ + ap_register_input_filter(filter->name, ef_input_filter, NULL, AP_FTYPE_RESOURCE); + } +#endif + else { + ap_assert(1 != 1); /* we set the field wrong somehow */ + } + + return NULL; +} + +static const command_rec cmds[] = +{ + AP_INIT_ITERATE("ExtFilterOptions", + add_options, + NULL, + ACCESS_CONF, /* same as SetInputFilter/SetOutputFilter */ + "valid options: DebugLevel=n, LogStderr, NoLogStderr"), + AP_INIT_RAW_ARGS("ExtFilterDefine", + define_filter, + NULL, + RSRC_CONF, + "Define an external filter"), + {NULL} +}; + +static int ef_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *main_s) +{ + main_server = main_s; + return OK; +} + +static void register_hooks(apr_pool_t *p) +{ + ap_hook_post_config(ef_init, NULL, NULL, APR_HOOK_MIDDLE); +} + +static apr_status_t set_resource_limits(request_rec *r, + apr_procattr_t *procattr) +{ +#if defined(RLIMIT_CPU) || defined(RLIMIT_NPROC) || \ + defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS) + core_dir_config *conf = + (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + apr_status_t rv; + +#ifdef RLIMIT_CPU + rv = apr_procattr_limit_set(procattr, APR_LIMIT_CPU, conf->limit_cpu); + ap_assert(rv == APR_SUCCESS); /* otherwise, we're out of sync with APR */ +#endif +#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) + rv = apr_procattr_limit_set(procattr, APR_LIMIT_MEM, conf->limit_mem); + ap_assert(rv == APR_SUCCESS); /* otherwise, we're out of sync with APR */ +#endif +#ifdef RLIMIT_NPROC + rv = apr_procattr_limit_set(procattr, APR_LIMIT_NPROC, conf->limit_nproc); + ap_assert(rv == APR_SUCCESS); /* otherwise, we're out of sync with APR */ +#endif + +#endif /* if at least one limit defined */ + + return APR_SUCCESS; +} + +static apr_status_t ef_close_file(void *vfile) +{ + return apr_file_close(vfile); +} + +static void child_errfn(apr_pool_t *pool, apr_status_t err, const char *description) +{ + request_rec *r; + void *vr; + apr_file_t *stderr_log; + char errbuf[200]; + char time_str[APR_CTIME_LEN]; + + apr_pool_userdata_get(&vr, ERRFN_USERDATA_KEY, pool); + r = vr; + apr_file_open_stderr(&stderr_log, pool); + ap_recent_ctime(time_str, apr_time_now()); + apr_file_printf(stderr_log, + "[%s] [client %s] mod_ext_filter (%d)%s: %s\n", + time_str, + r->connection->remote_ip, + err, + apr_strerror(err, errbuf, sizeof(errbuf)), + description); +} + +/* init_ext_filter_process: get the external filter process going + * This is per-filter-instance (i.e., per-request) initialization. + */ +static apr_status_t init_ext_filter_process(ap_filter_t *f) +{ + ef_ctx_t *ctx = f->ctx; + apr_status_t rc; + ef_dir_t *dc = ctx->dc; + const char * const *env; + + ctx->proc = apr_pcalloc(ctx->p, sizeof(*ctx->proc)); + + rc = apr_procattr_create(&ctx->procattr, ctx->p); + ap_assert(rc == APR_SUCCESS); + + rc = apr_procattr_io_set(ctx->procattr, + APR_CHILD_BLOCK, + APR_CHILD_BLOCK, + APR_CHILD_BLOCK); + ap_assert(rc == APR_SUCCESS); + + rc = set_resource_limits(f->r, ctx->procattr); + ap_assert(rc == APR_SUCCESS); + + if (dc->log_stderr > 0) { + rc = apr_procattr_child_err_set(ctx->procattr, + f->r->server->error_log, /* stderr in child */ + NULL); + ap_assert(rc == APR_SUCCESS); + } + + rc = apr_procattr_child_errfn_set(ctx->procattr, child_errfn); + ap_assert(rc == APR_SUCCESS); + apr_pool_userdata_set(f->r, ERRFN_USERDATA_KEY, apr_pool_cleanup_null, ctx->p); + + if (dc->debug >= DBGLVL_ERRORCHECK) { + rc = apr_procattr_error_check_set(ctx->procattr, 1); + ap_assert(rc == APR_SUCCESS); + } + + /* add standard CGI variables as well as DOCUMENT_URI, DOCUMENT_PATH_INFO, + * and QUERY_STRING_UNESCAPED + */ + ap_add_cgi_vars(f->r); + ap_add_common_vars(f->r); + apr_table_setn(f->r->subprocess_env, "DOCUMENT_URI", f->r->uri); + apr_table_setn(f->r->subprocess_env, "DOCUMENT_PATH_INFO", f->r->path_info); + if (f->r->args) { + /* QUERY_STRING is added by ap_add_cgi_vars */ + char *arg_copy = apr_pstrdup(f->r->pool, f->r->args); + ap_unescape_url(arg_copy); + apr_table_setn(f->r->subprocess_env, "QUERY_STRING_UNESCAPED", + ap_escape_shell_cmd(f->r->pool, arg_copy)); + } + + env = (const char * const *) ap_create_environment(ctx->p, + f->r->subprocess_env); + + rc = apr_proc_create(ctx->proc, + ctx->filter->command, + (const char * const *)ctx->filter->args, + env, /* environment */ + ctx->procattr, + ctx->p); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, f->r, + "couldn't create child process to run `%s'", + ctx->filter->command); + return rc; + } + + apr_pool_note_subprocess(ctx->p, ctx->proc, APR_KILL_AFTER_TIMEOUT); + + /* We don't want the handle to the child's stdin inherited by any + * other processes created by httpd. Otherwise, when we close our + * handle, the child won't see EOF because another handle will still + * be open. + */ + + apr_pool_cleanup_register(ctx->p, ctx->proc->in, + apr_pool_cleanup_null, /* other mechanism */ + ef_close_file); + +#if APR_FILES_AS_SOCKETS + { + apr_socket_t *newsock; + + rc = apr_poll_setup(&ctx->pollset, 2, ctx->p); + ap_assert(rc == APR_SUCCESS); + rc = apr_socket_from_file(&newsock, ctx->proc->in); + ap_assert(rc == APR_SUCCESS); + rc = apr_poll_socket_add(ctx->pollset, newsock, APR_POLLOUT); + ap_assert(rc == APR_SUCCESS); + rc = apr_socket_from_file(&newsock, ctx->proc->out); + ap_assert(rc == APR_SUCCESS); + rc = apr_poll_socket_add(ctx->pollset, newsock, APR_POLLIN); + ap_assert(rc == APR_SUCCESS); + } +#endif + + return APR_SUCCESS; +} + +static const char *get_cfg_string(ef_dir_t *dc, ef_filter_t *filter, apr_pool_t *p) +{ + const char *debug_str = dc->debug == -1 ? + "DebugLevel=0" : apr_psprintf(p, "DebugLevel=%d", dc->debug); + const char *log_stderr_str = dc->log_stderr < 1 ? + "NoLogStderr" : "LogStderr"; + const char *preserve_content_length_str = filter->preserves_content_length ? + "PreservesContentLength" : "!PreserveContentLength"; + const char *intype_str = !filter->intype ? + "*/*" : filter->intype; + const char *outtype_str = !filter->outtype ? + "(unchanged)" : filter->outtype; + + return apr_psprintf(p, + "ExtFilterOptions %s %s %s ExtFilterInType %s " + "ExtFilterOuttype %s", + debug_str, log_stderr_str, preserve_content_length_str, + intype_str, outtype_str); +} + +static ef_filter_t *find_filter_def(const server_rec *s, const char *fname) +{ + ef_server_t *sc; + ef_filter_t *f; + + sc = ap_get_module_config(s->module_config, &ext_filter_module); + f = apr_hash_get(sc->h, fname, APR_HASH_KEY_STRING); + if (!f && s != main_server) { + s = main_server; + sc = ap_get_module_config(s->module_config, &ext_filter_module); + f = apr_hash_get(sc->h, fname, APR_HASH_KEY_STRING); + } + return f; +} + +static apr_status_t init_filter_instance(ap_filter_t *f) +{ + ef_ctx_t *ctx; + ef_dir_t *dc; + apr_status_t rv; + + f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(ef_ctx_t)); + dc = ap_get_module_config(f->r->per_dir_config, + &ext_filter_module); + ctx->dc = dc; + /* look for the user-defined filter */ + ctx->filter = find_filter_def(f->r->server, f->frec->name); + if (!ctx->filter) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, + "couldn't find definition of filter '%s'", + f->frec->name); + return APR_EINVAL; + } + ctx->p = f->r->pool; + if (ctx->filter->intype && + ctx->filter->intype != INTYPE_ALL) { + if (!f->r->content_type) { + ctx->noop = 1; + } + else { + const char *ctypes = f->r->content_type; + const char *ctype = ap_getword(f->r->pool, &ctypes, ';'); + + if (strcasecmp(ctx->filter->intype, ctype)) { + /* wrong IMT for us; don't mess with the output */ + ctx->noop = 1; + } + } + } + if (ctx->filter->enable_env && + !apr_table_get(f->r->subprocess_env, ctx->filter->enable_env)) { + /* an environment variable that enables the filter isn't set; bail */ + ctx->noop = 1; + } + if (ctx->filter->disable_env && + apr_table_get(f->r->subprocess_env, ctx->filter->disable_env)) { + /* an environment variable that disables the filter is set; bail */ + ctx->noop = 1; + } + if (!ctx->noop) { + rv = init_ext_filter_process(f); + if (rv != APR_SUCCESS) { + return rv; + } + if (ctx->filter->outtype && + ctx->filter->outtype != OUTTYPE_UNCHANGED) { + ap_set_content_type(f->r, ctx->filter->outtype); + } + if (ctx->filter->preserves_content_length != 1) { + /* nasty, but needed to avoid confusing the browser + */ + apr_table_unset(f->r->headers_out, "Content-Length"); + } + } + + if (dc->debug >= DBGLVL_SHOWOPTIONS) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, + "%sfiltering `%s' of type `%s' through `%s', cfg %s", + ctx->noop ? "NOT " : "", + f->r->uri ? f->r->uri : f->r->filename, + f->r->content_type ? f->r->content_type : "(unspecified)", + ctx->filter->command, + get_cfg_string(dc, ctx->filter, f->r->pool)); + } + + return APR_SUCCESS; +} + +/* drain_available_output(): + * + * if any data is available from the filter, read it and pass it + * to the next filter + */ +static apr_status_t drain_available_output(ap_filter_t *f) +{ + request_rec *r = f->r; + conn_rec *c = r->connection; + ef_ctx_t *ctx = f->ctx; + ef_dir_t *dc = ctx->dc; + apr_size_t len; + char buf[4096]; + apr_status_t rv; + apr_bucket_brigade *bb; + apr_bucket *b; + + while (1) { + len = sizeof(buf); + rv = apr_file_read(ctx->proc->out, + buf, + &len); + if ((rv && !APR_STATUS_IS_EAGAIN(rv)) || + dc->debug >= DBGLVL_GORY) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, + "apr_file_read(child output), len %" APR_SIZE_T_FMT, + !rv ? len : -1); + } + if (rv != APR_SUCCESS) { + return rv; + } + bb = apr_brigade_create(r->pool, c->bucket_alloc); + b = apr_bucket_transient_create(buf, len, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, b); + if ((rv = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "ap_pass_brigade()"); + return rv; + } + } + /* we should never get here; if we do, a bogus error message would be + * the least of our problems + */ + return APR_ANONYMOUS; +} + +static apr_status_t pass_data_to_filter(ap_filter_t *f, const char *data, + apr_size_t len) +{ + ef_ctx_t *ctx = f->ctx; + ef_dir_t *dc = ctx->dc; + apr_status_t rv; + apr_size_t bytes_written = 0; + apr_size_t tmplen; + + do { + tmplen = len - bytes_written; + rv = apr_file_write(ctx->proc->in, + (const char *)data + bytes_written, + &tmplen); + bytes_written += tmplen; + if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, + "apr_file_write(child input), len %" APR_SIZE_T_FMT, + tmplen); + return rv; + } + if (APR_STATUS_IS_EAGAIN(rv)) { + /* XXX handle blocking conditions here... if we block, we need + * to read data from the child process and pass it down to the + * next filter! + */ + rv = drain_available_output(f); + if (APR_STATUS_IS_EAGAIN(rv)) { +#if APR_FILES_AS_SOCKETS + int num_events; + + rv = apr_poll(ctx->pollset, 2, + &num_events, f->r->server->timeout); + if (rv || dc->debug >= DBGLVL_GORY) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, + rv, f->r, "apr_poll()"); + } + if (rv != APR_SUCCESS && !APR_STATUS_IS_EINTR(rv)) { + /* some error such as APR_TIMEUP */ + return rv; + } +#else /* APR_FILES_AS_SOCKETS */ + /* Yuck... I'd really like to wait until I can read + * or write, but instead I have to sleep and try again + */ + apr_sleep(100000); /* 100 milliseconds */ + if (dc->debug >= DBGLVL_GORY) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, + 0, f->r, "apr_sleep()"); + } +#endif /* APR_FILES_AS_SOCKETS */ + } + else if (rv != APR_SUCCESS) { + return rv; + } + } + } while (bytes_written < len); + return rv; +} + +static apr_status_t ef_output_filter(ap_filter_t *f, apr_bucket_brigade *bb) +{ + request_rec *r = f->r; + conn_rec *c = r->connection; + ef_ctx_t *ctx = f->ctx; + apr_bucket *b; + ef_dir_t *dc; + apr_size_t len; + const char *data; + apr_status_t rv; + char buf[4096]; + apr_bucket *eos = NULL; + + if (!ctx) { + if ((rv = init_filter_instance(f)) != APR_SUCCESS) { + return rv; + } + ctx = f->ctx; + } + if (ctx->noop) { + ap_remove_output_filter(f); + return ap_pass_brigade(f->next, bb); + } + dc = ctx->dc; + + APR_BRIGADE_FOREACH(b, bb) { + + if (APR_BUCKET_IS_EOS(b)) { + eos = b; + break; + } + + rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "apr_bucket_read()"); + return rv; + } + + /* Good cast, we just tested len isn't negative */ + if (len > 0 && + (rv = pass_data_to_filter(f, data, (apr_size_t)len)) + != APR_SUCCESS) { + return rv; + } + } + + apr_brigade_destroy(bb); + + /* XXX What we *really* need to do once we've hit eos is create a pipe bucket + * from the child output pipe and pass down the pipe bucket + eos. + */ + if (eos) { + /* close the child's stdin to signal that no more data is coming; + * that will cause the child to finish generating output + */ + if ((rv = apr_file_close(ctx->proc->in)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "apr_file_close(child input)"); + return rv; + } + /* since we've seen eos and closed the child's stdin, set the proper pipe + * timeout; we don't care if we don't return from apr_file_read() for a while... + */ + rv = apr_file_pipe_timeout_set(ctx->proc->out, + r->server->timeout); + if (rv) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "apr_file_pipe_timeout_set(child output)"); + return rv; + } + } + + do { + len = sizeof(buf); + rv = apr_file_read(ctx->proc->out, + buf, + &len); + if ((rv && !APR_STATUS_IS_EOF(rv) && !APR_STATUS_IS_EAGAIN(rv)) || + dc->debug >= DBGLVL_GORY) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, + "apr_file_read(child output), len %" APR_SIZE_T_FMT, + !rv ? len : -1); + } + if (APR_STATUS_IS_EAGAIN(rv)) { + if (eos) { + /* should not occur, because we have an APR timeout in place */ + AP_DEBUG_ASSERT(1 != 1); + } + return APR_SUCCESS; + } + + if (rv == APR_SUCCESS) { + bb = apr_brigade_create(r->pool, c->bucket_alloc); + b = apr_bucket_transient_create(buf, len, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, b); + if ((rv = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "ap_pass_brigade(filtered buffer) failed"); + return rv; + } + } + } while (rv == APR_SUCCESS); + + if (!APR_STATUS_IS_EOF(rv)) { + return rv; + } + + if (eos) { + /* pass down eos */ + bb = apr_brigade_create(r->pool, c->bucket_alloc); + b = apr_bucket_eos_create(c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, b); + if ((rv = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "ap_pass_brigade(eos) failed"); + return rv; + } + } + + return APR_SUCCESS; +} + +#if 0 +static int ef_input_filter(ap_filter_t *f, apr_bucket_brigade *bb, + ap_input_mode_t mode, apr_read_type_e block, + apr_off_t readbytes) +{ + apr_status_t rv; + apr_bucket *b; + char *buf; + apr_ssize_t len; + char *zero; + + rv = ap_get_brigade(f->next, bb, mode, block, readbytes); + if (rv != APR_SUCCESS) { + return rv; + } + + APR_BRIGADE_FOREACH(b, bb) { + if (!APR_BUCKET_IS_EOS(b)) { + if ((rv = apr_bucket_read(b, (const char **)&buf, &len, APR_BLOCK_READ)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, "apr_bucket_read() failed"); + return rv; + } + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "apr_bucket_read -> %d bytes", + len); + while ((zero = memchr(buf, '0', len))) { + *zero = 'a'; + } + } + else + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "got eos bucket"); + } + + return rv; +} +#endif + +module AP_MODULE_DECLARE_DATA ext_filter_module = +{ + STANDARD20_MODULE_STUFF, + create_ef_dir_conf, + merge_ef_dir_conf, + create_ef_server_conf, + NULL, + cmds, + register_hooks +}; diff --git a/rubbos/app/httpd-2.0.64/modules/filters/mod_ext_filter.dsp b/rubbos/app/httpd-2.0.64/modules/filters/mod_ext_filter.dsp new file mode 100644 index 00000000..fccf6e8b --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/mod_ext_filter.dsp @@ -0,0 +1,128 @@ +# Microsoft Developer Studio Project File - Name="mod_ext_filter" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_ext_filter - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_ext_filter.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_ext_filter.mak" CFG="mod_ext_filter - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_ext_filter - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_ext_filter - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_ext_filter - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /Zi /O2 /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_ext_filter_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:"Release/mod_ext_filter.so" /base:@..\..\os\win32\BaseAddr.ref,mod_ext_filter.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Release/mod_ext_filter.so" /base:@..\..\os\win32\BaseAddr.ref,mod_ext_filter.so /opt:ref + +!ELSEIF "$(CFG)" == "mod_ext_filter - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_ext_filter_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_ext_filter.so" /base:@..\..\os\win32\BaseAddr.ref,mod_ext_filter.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_ext_filter.so" /base:@..\..\os\win32\BaseAddr.ref,mod_ext_filter.so + +!ENDIF + +# Begin Target + +# Name "mod_ext_filter - Win32 Release" +# Name "mod_ext_filter - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_ext_filter.c +# End Source File +# Begin Source File + +SOURCE=.\mod_ext_filter.rc +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\win32ver.awk + +!IF "$(CFG)" == "mod_ext_filter - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_ext_filter.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_ext_filter.so "ext_filter_module for Apache" ../../include/ap_release.h > .\mod_ext_filter.rc + +# End Custom Build + +!ELSEIF "$(CFG)" == "mod_ext_filter - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_ext_filter.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_ext_filter.so "ext_filter_module for Apache" ../../include/ap_release.h > .\mod_ext_filter.rc + +# End Custom Build + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/rubbos/app/httpd-2.0.64/modules/filters/mod_ext_filter.exp b/rubbos/app/httpd-2.0.64/modules/filters/mod_ext_filter.exp new file mode 100644 index 00000000..ed3b8fc6 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/mod_ext_filter.exp @@ -0,0 +1 @@ +ext_filter_module diff --git a/rubbos/app/httpd-2.0.64/modules/filters/mod_include.c b/rubbos/app/httpd-2.0.64/modules/filters/mod_include.c new file mode 100644 index 00000000..38dc3213 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/mod_include.c @@ -0,0 +1,3751 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * http_include.c: Handles the server-parsed HTML documents + * + * Original by Rob McCool; substantial fixups by David Robinson; + * incorporated into the Apache module framework by rst. + * + */ + +#include "apr.h" +#include "apr_strings.h" +#include "apr_thread_proc.h" +#include "apr_hash.h" +#include "apr_user.h" +#include "apr_lib.h" +#include "apr_optional.h" + +#define APR_WANT_STRFUNC +#define APR_WANT_MEMFUNC +#include "apr_want.h" + +#define CORE_PRIVATE + +#include "ap_config.h" +#include "util_filter.h" +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_log.h" +#include "http_main.h" +#include "util_script.h" +#include "http_core.h" + +#define MOD_INCLUDE_REDESIGN +#include "mod_include.h" +#include "util_ebcdic.h" + +module AP_MODULE_DECLARE_DATA include_module; +static apr_hash_t *include_hash; +static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *ssi_pfn_register; + +/***************************************************************** + * + * XBITHACK. Sigh... NB it's configurable per-directory; the compile-time + * option only changes the default. + */ + +enum xbithack { + xbithack_off, xbithack_on, xbithack_full +}; + +struct bndm_t { + unsigned int T[256]; + unsigned int x; +} ; + +typedef struct { + char *default_error_msg; + char *default_time_fmt; + enum xbithack *xbithack; +} include_dir_config; + +typedef struct { + char *default_start_tag; + char *default_end_tag; + int start_tag_len; + bndm_t start_seq_pat; + char *undefinedEcho; + int undefinedEchoLen; +} include_server_config; + +/* main parser states */ +typedef enum { + PARSE_PRE_HEAD, + PARSE_HEAD, + PARSE_DIRECTIVE, + PARSE_DIRECTIVE_POSTNAME, + PARSE_DIRECTIVE_TAIL, + PARSE_DIRECTIVE_POSTTAIL, + PARSE_PRE_ARG, + PARSE_ARG, + PARSE_ARG_NAME, + PARSE_ARG_POSTNAME, + PARSE_ARG_EQ, + PARSE_ARG_PREVAL, + PARSE_ARG_VAL, + PARSE_ARG_VAL_ESC, + PARSE_ARG_POSTVAL, + PARSE_TAIL, + PARSE_TAIL_SEQ, + PARSE_EXECUTE +} parse_state_t; + +typedef struct ssi_arg_item { + struct ssi_arg_item *next; + char *name; + apr_size_t name_len; + char *value; + apr_size_t value_len; +} ssi_arg_item_t; + +typedef struct { + parse_state_t state; + int seen_eos; + int error; + char quote; /* quote character value (or \0) */ + + apr_bucket_brigade *tmp_bb; + + apr_size_t end_seq_len; + char *directive; /* name of the current directive */ + + unsigned argc; /* argument counter (of the current + * directive) + */ + ssi_arg_item_t *argv; /* all arguments */ + ssi_arg_item_t *current_arg; /* currently parsed argument */ + request_rec *r; + include_ctx_t *ctx; /* public part of the context structure */ + + apr_pool_t *dpool; +} ssi_ctx_t; + +#ifdef XBITHACK +#define DEFAULT_XBITHACK xbithack_full +#else +#define DEFAULT_XBITHACK xbithack_off +#endif + +#define BYTE_COUNT_THRESHOLD AP_MIN_BYTES_TO_WRITE + +#define SSI_CREATE_ERROR_BUCKET(ctx, f, bb) APR_BRIGADE_INSERT_TAIL((bb), \ + apr_bucket_pool_create(apr_pstrdup((ctx)->pool, (ctx)->error_str), \ + strlen((ctx)->error_str), (ctx)->pool, \ + (f)->c->bucket_alloc)) + +/* ------------------------ Environment function -------------------------- */ + +/* Sentinel value to store in subprocess_env for items that + * shouldn't be evaluated until/unless they're actually used + */ +static const char lazy_eval_sentinel; +#define LAZY_VALUE (&lazy_eval_sentinel) + +static void add_include_vars(request_rec *r, char *timefmt) +{ + apr_table_t *e = r->subprocess_env; + char *t; + + apr_table_setn(e, "DATE_LOCAL", LAZY_VALUE); + apr_table_setn(e, "DATE_GMT", LAZY_VALUE); + apr_table_setn(e, "LAST_MODIFIED", LAZY_VALUE); + apr_table_setn(e, "DOCUMENT_URI", r->uri); + if (r->path_info && *r->path_info) { + apr_table_setn(e, "DOCUMENT_PATH_INFO", r->path_info); + } + apr_table_setn(e, "USER_NAME", LAZY_VALUE); + if (r->filename && (t = strrchr(r->filename, '/'))) { + apr_table_setn(e, "DOCUMENT_NAME", ++t); + } + else { + apr_table_setn(e, "DOCUMENT_NAME", r->uri); + } + if (r->args) { + char *arg_copy = apr_pstrdup(r->pool, r->args); + + ap_unescape_url(arg_copy); + apr_table_setn(e, "QUERY_STRING_UNESCAPED", + ap_escape_shell_cmd(r->pool, arg_copy)); + } +} + +static const char *add_include_vars_lazy(request_rec *r, const char *var) +{ + char *val; + if (!strcasecmp(var, "DATE_LOCAL")) { + include_dir_config *conf = + (include_dir_config *)ap_get_module_config(r->per_dir_config, + &include_module); + val = ap_ht_time(r->pool, r->request_time, conf->default_time_fmt, 0); + } + else if (!strcasecmp(var, "DATE_GMT")) { + include_dir_config *conf = + (include_dir_config *)ap_get_module_config(r->per_dir_config, + &include_module); + val = ap_ht_time(r->pool, r->request_time, conf->default_time_fmt, 1); + } + else if (!strcasecmp(var, "LAST_MODIFIED")) { + include_dir_config *conf = + (include_dir_config *)ap_get_module_config(r->per_dir_config, + &include_module); + val = ap_ht_time(r->pool, r->finfo.mtime, conf->default_time_fmt, 0); + } + else if (!strcasecmp(var, "USER_NAME")) { + if (apr_get_username(&val, r->finfo.user, r->pool) != APR_SUCCESS) { + val = "<unknown>"; + } + } + else { + val = NULL; + } + + if (val) { + apr_table_setn(r->subprocess_env, var, val); + } + return val; +} + +static const char *get_include_var(request_rec *r, include_ctx_t *ctx, + const char *var) +{ + const char *val; + if (apr_isdigit(*var) && !var[1]) { + /* Handle $0 .. $9 from the last regex evaluated. + * The choice of returning NULL strings on not-found, + * v.s. empty strings on an empty match is deliberate. + */ + if (!ctx->re_result || !ctx->re_string) { + return NULL; + } + else { + int idx = atoi(var); + apr_size_t len = (*ctx->re_result)[idx].rm_eo + - (*ctx->re_result)[idx].rm_so; + if ( (*ctx->re_result)[idx].rm_so < 0 + || (*ctx->re_result)[idx].rm_eo < 0) { + return NULL; + } + val = apr_pstrmemdup(r->pool, ctx->re_string + + (*ctx->re_result)[idx].rm_so, len); + } + } + else { + val = apr_table_get(r->subprocess_env, var); + + if (val == LAZY_VALUE) + val = add_include_vars_lazy(r, var); + } + return val; +} + +/* --------------------------- Parser functions --------------------------- */ + +/* This is an implementation of the BNDM search algorithm. + * + * Fast and Flexible String Matching by Combining Bit-parallelism and + * Suffix Automata (2001) + * Gonzalo Navarro, Mathieu Raffinot + * + * http://www-igm.univ-mlv.fr/~raffinot/ftp/jea2001.ps.gz + * + * Initial code submitted by Sascha Schumann. + */ + +/* Precompile the bndm_t data structure. */ +static void bndm_compile(bndm_t *t, const char *n, apr_size_t nl) +{ + unsigned int x; + const char *ne = n + nl; + + memset(t->T, 0, sizeof(unsigned int) * 256); + + for (x = 1; n < ne; x <<= 1) + t->T[(unsigned char) *n++] |= x; + + t->x = x - 1; +} + +/* Implements the BNDM search algorithm (as described above). + * + * n - the pattern to search for + * nl - length of the pattern to search for + * h - the string to look in + * hl - length of the string to look for + * t - precompiled bndm structure against the pattern + * + * Returns the count of character that is the first match or hl if no + * match is found. + */ +static apr_size_t bndm(const char *n, apr_size_t nl, const char *h, + apr_size_t hl, bndm_t *t) +{ + const char *skip; + const char *he, *p, *pi; + unsigned int *T, x, d; + + he = h + hl; + + T = t->T; + x = t->x; + + pi = h - 1; /* pi: p initial */ + p = pi + nl; /* compare window right to left. point to the first char */ + + while (p < he) { + skip = p; + d = x; + do { + d &= T[(unsigned char) *p--]; + if (!d) { + break; + } + if ((d & 1)) { + if (p != pi) + skip = p; + else + return p - h + 1; + } + d >>= 1; + } while (d); + + pi = skip; + p = pi + nl; + } + + return hl; +} + +/* + * decodes a string containing html entities or numeric character references. + * 's' is overwritten with the decoded string. + * If 's' is syntatically incorrect, then the followed fixups will be made: + * unknown entities will be left undecoded; + * references to unused numeric characters will be deleted. + * In particular, � will not be decoded, but will be deleted. + * + * drtr + */ + +/* maximum length of any ISO-LATIN-1 HTML entity name. */ +#define MAXENTLEN (6) + +/* The following is a shrinking transformation, therefore safe. */ + +static void decodehtml(char *s) +{ + int val, i, j; + char *p; + const char *ents; + static const char * const entlist[MAXENTLEN + 1] = + { + NULL, /* 0 */ + NULL, /* 1 */ + "lt\074gt\076", /* 2 */ + "amp\046ETH\320eth\360", /* 3 */ + "quot\042Auml\304Euml\313Iuml\317Ouml\326Uuml\334auml\344euml\353\ +iuml\357ouml\366uuml\374yuml\377", /* 4 */ + "Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc\333\ +THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352icirc\356ocirc\364\ +ucirc\373thorn\376", /* 5 */ + "Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311\ +Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde\325Oslash\330\ +Ugrave\331Uacute\332Yacute\335agrave\340aacute\341atilde\343ccedil\347\ +egrave\350eacute\351igrave\354iacute\355ntilde\361ograve\362oacute\363\ +otilde\365oslash\370ugrave\371uacute\372yacute\375" /* 6 */ + }; + + /* Do a fast scan through the string until we find anything + * that needs more complicated handling + */ + for (; *s != '&'; s++) { + if (*s == '\0') { + return; + } + } + + for (p = s; *s != '\0'; s++, p++) { + if (*s != '&') { + *p = *s; + continue; + } + /* find end of entity */ + for (i = 1; s[i] != ';' && s[i] != '\0'; i++) { + continue; + } + + if (s[i] == '\0') { /* treat as normal data */ + *p = *s; + continue; + } + + /* is it numeric ? */ + if (s[1] == '#') { + for (j = 2, val = 0; j < i && apr_isdigit(s[j]); j++) { + val = val * 10 + s[j] - '0'; + } + s += i; + if (j < i || val <= 8 || (val >= 11 && val <= 31) || + (val >= 127 && val <= 160) || val >= 256) { + p--; /* no data to output */ + } + else { + *p = RAW_ASCII_CHAR(val); + } + } + else { + j = i - 1; + if (j > MAXENTLEN || entlist[j] == NULL) { + /* wrong length */ + *p = '&'; + continue; /* skip it */ + } + for (ents = entlist[j]; *ents != '\0'; ents += i) { + if (strncmp(s + 1, ents, j) == 0) { + break; + } + } + + if (*ents == '\0') { + *p = '&'; /* unknown */ + } + else { + *p = RAW_ASCII_CHAR(((const unsigned char *) ents)[j]); + s += i; + } + } + } + + *p = '\0'; +} + +/* + * Extract the next tag name and value. + * If there are no more tags, set the tag name to NULL. + * The tag value is html decoded if dodecode is non-zero. + * The tag value may be NULL if there is no tag value.. + * format: + * [WS]<Tag>[WS]=[WS]['|"|`]<Value>[['|"|`|]|WS] + */ + +#define SKIP_TAG_WHITESPACE(ptr) while ((*ptr != '\0') && (apr_isspace (*ptr))) ptr++ + +static void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag, + char **tag_val, int dodecode) +{ + *tag_val = NULL; + if (ctx->curr_tag_pos >= ctx->combined_tag + ctx->tag_length) { + *tag = NULL; + return; + } + + *tag = ctx->curr_tag_pos; + if (!**tag) { + *tag = NULL; + /* finitio */ + ctx->curr_tag_pos = ctx->combined_tag + ctx->tag_length; + return; + } + + *tag_val = ap_strchr(*tag, '='); + if (!*tag_val) { + ctx->curr_tag_pos = ctx->combined_tag + ctx->tag_length; + return; + } + + /* if it starts with '=' there was no tag name, just a value */ + if (*tag_val == *tag) { + *tag = NULL; + } + + *(*tag_val)++ = '\0'; + ctx->curr_tag_pos = *tag_val + strlen(*tag_val) + 1; /* skip \0 byte */ + + if (dodecode) { + decodehtml(*tag_val); + } + + return; +} + +/* initial buffer size for power-of-two allocator in ap_ssi_parse_string */ +#define PARSE_STRING_INITIAL_SIZE 64 + +/* + * Do variable substitution on strings + * (Note: If out==NULL, this function allocs a buffer for the resulting + * string from r->pool. The return value is the parsed string) + */ +static char *ap_ssi_parse_string(request_rec *r, include_ctx_t *ctx, + const char *in, char *out, + apr_size_t length, int leave_name) +{ + char ch; + char *next; + char *end_out; + apr_size_t out_size; + + /* allocate an output buffer if needed */ + if (!out) { + out_size = PARSE_STRING_INITIAL_SIZE; + if (out_size > length) { + out_size = length; + } + out = apr_palloc(r->pool, out_size); + } + else { + out_size = length; + } + + /* leave room for nul terminator */ + end_out = out + out_size - 1; + + next = out; + while ((ch = *in++) != '\0') { + switch (ch) { + case '\\': + if (next == end_out) { + if (out_size < length) { + /* double the buffer size */ + apr_size_t new_out_size = out_size * 2; + apr_size_t current_length = next - out; + char *new_out; + if (new_out_size > length) { + new_out_size = length; + } + new_out = apr_palloc(r->pool, new_out_size); + memcpy(new_out, out, current_length); + out = new_out; + out_size = new_out_size; + end_out = out + out_size - 1; + next = out + current_length; + } + else { + /* truncated */ + *next = '\0'; + return out; + } + } + if (*in == '$') { + *next++ = *in++; + } + else { + *next++ = ch; + } + break; + case '$': + { + const char *start_of_var_name; + char *end_of_var_name; /* end of var name + 1 */ + const char *expansion, *temp_end, *val; + char tmp_store; + apr_size_t l; + + /* guess that the expansion won't happen */ + expansion = in - 1; + if (*in == '{') { + ++in; + start_of_var_name = in; + in = ap_strchr_c(in, '}'); + if (in == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, + 0, r, "Missing '}' on variable \"%s\"", + expansion); + *next = '\0'; + return out; + } + temp_end = in; + end_of_var_name = (char *)temp_end; + ++in; + } + else { + start_of_var_name = in; + while (apr_isalnum(*in) || *in == '_') { + ++in; + } + temp_end = in; + end_of_var_name = (char *)temp_end; + } + /* what a pain, too bad there's no table_getn where you can + * pass a non-nul terminated string */ + l = end_of_var_name - start_of_var_name; + if (l != 0) { + tmp_store = *end_of_var_name; + *end_of_var_name = '\0'; + val = get_include_var(r, ctx, start_of_var_name); + *end_of_var_name = tmp_store; + + if (val) { + expansion = val; + l = strlen(expansion); + } + else if (leave_name) { + l = in - expansion; + } + else { + /* no expansion to be done */ + break; + } + } + else { + /* zero-length variable name causes just the $ to be + * copied */ + l = 1; + } + if ((next + l > end_out) && (out_size < length)) { + /* increase the buffer size to accommodate l more chars */ + apr_size_t new_out_size = out_size; + apr_size_t current_length = next - out; + char *new_out; + do { + new_out_size *= 2; + } while (new_out_size < current_length + l + 1); /* +1 for NUL */ + if (new_out_size > length) { + new_out_size = length; + } + new_out = apr_palloc(r->pool, new_out_size); + memcpy(new_out, out, current_length); + out = new_out; + out_size = new_out_size; + end_out = out + out_size - 1; + next = out + current_length; + } + l = ((int)l > end_out - next) ? (end_out - next) : l; + memcpy(next, expansion, l); + next += l; + break; + } + default: + if (next == end_out) { + if (out_size < length) { + /* double the buffer size */ + apr_size_t new_out_size = out_size * 2; + apr_size_t current_length = next - out; + char *new_out; + if (new_out_size > length) { + new_out_size = length; + } + new_out = apr_palloc(r->pool, new_out_size); + memcpy(new_out, out, current_length); + out = new_out; + out_size = new_out_size; + end_out = out + out_size - 1; + next = out + current_length; + } + else { + /* truncated */ + *next = '\0'; + return out; + } + } + *next++ = ch; + break; + } + } + *next = '\0'; + return out; +} + +/* --------------------------- Action handlers ---------------------------- */ + +/* ensure that path is relative, and does not contain ".." elements + * ensentially ensure that it does not match the regex: + * (^/|(^|/)\.\.(/|$)) + * XXX: Simply replace with apr_filepath_merge + */ +static int is_only_below(const char *path) +{ +#ifdef HAVE_DRIVE_LETTERS + if (path[1] == ':') + return 0; +#endif +#ifdef NETWARE + if (ap_strchr_c(path, ':')) + return 0; +#endif + if (path[0] == '/') { + return 0; + } + while (*path) { + int dots = 0; + while (path[dots] == '.') + ++dots; +#if defined(WIN32) + /* If the name is canonical this is redundant + * but in security, redundancy is worthwhile. + * Does OS2 belong here (accepts ... for ..)? + */ + if (dots > 1 && (!path[dots] || path[dots] == '/')) + return 0; +#else + if (dots == 2 && (!path[dots] || path[dots] == '/')) + return 0; +#endif + path += dots; + /* Advance to either the null byte at the end of the + * string or the character right after the next slash, + * whichever comes first + */ + while (*path && (*path++ != '/')) { + continue; + } + } + return 1; +} + +static int handle_include(include_ctx_t *ctx, apr_bucket_brigade **bb, + request_rec *r, ap_filter_t *f, apr_bucket *head_ptr, + apr_bucket **inserted_head) +{ + char *tag = NULL; + char *tag_val = NULL; + apr_bucket *tmp_buck; + char *parsed_string; + int loglevel = APLOG_ERR; + + *inserted_head = NULL; + if (ctx->flags & FLAG_PRINTING) { + while (1) { + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); + if (tag_val == NULL) { + if (tag == NULL) { + return (0); + } + else { + return (1); + } + } + if (!strcmp(tag, "virtual") || !strcmp(tag, "file")) { + request_rec *rr = NULL; + char *error_fmt = NULL; + apr_status_t rc = APR_SUCCESS; + + SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx, f->next, rc); + if (rc != APR_SUCCESS) { + return rc; + } + + parsed_string = ap_ssi_parse_string(r, ctx, tag_val, NULL, + MAX_STRING_LEN, 0); + if (tag[0] == 'f') { + /* XXX: Port to apr_filepath_merge + * be safe; only files in this directory or below allowed + */ + if (!is_only_below(parsed_string)) { + error_fmt = "unable to include file \"%s\" " + "in parsed file %s"; + } + else { + rr = ap_sub_req_lookup_uri(parsed_string, r, f->next); + } + } + else { + rr = ap_sub_req_lookup_uri(parsed_string, r, f->next); + } + + if (!error_fmt && rr->status != HTTP_OK) { + error_fmt = "unable to include \"%s\" in parsed file %s"; + } + + if (!error_fmt && (ctx->flags & FLAG_NO_EXEC) && + rr->content_type && + (strncmp(rr->content_type, "text/", 5))) { + error_fmt = "unable to include potential exec \"%s\" " + "in parsed file %s"; + } + + /* See the Kludge in send_parsed_file for why */ + /* Basically, it puts a bread crumb in here, then looks */ + /* for the crumb later to see if its been here. */ + if (rr) + ap_set_module_config(rr->request_config, + &include_module, r); + + if (!error_fmt && ap_run_sub_req(rr)) { + error_fmt = "unable to include \"%s\" in parsed file %s"; + } + if (error_fmt) { + ap_log_rerror(APLOG_MARK, loglevel, + 0, r, error_fmt, tag_val, r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, + *inserted_head); + } + + /* Do *not* destroy the subrequest here; it may have allocated + * variables in this r->subprocess_env in the subrequest's + * r->pool, so that pool must survive as long as this request. + * Yes, this is a memory leak. */ + } + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "unknown parameter \"%s\" to tag include in %s", + tag, r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); + return 1; + } + } + } + return 0; +} + + +static int handle_echo(include_ctx_t *ctx, apr_bucket_brigade **bb, + request_rec *r, ap_filter_t *f, apr_bucket *head_ptr, + apr_bucket **inserted_head) +{ + char *tag = NULL; + char *tag_val = NULL; + const char *echo_text = NULL; + apr_bucket *tmp_buck; + apr_size_t e_len; + enum {E_NONE, E_URL, E_ENTITY} encode; + + encode = E_ENTITY; + + *inserted_head = NULL; + if (ctx->flags & FLAG_PRINTING) { + while (1) { + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); + if (tag_val == NULL) { + if (tag != NULL) { + return 1; + } + else { + return 0; + } + } + if (!strcmp(tag, "var")) { + conn_rec *c = r->connection; + const char *val = + get_include_var(r, ctx, + ap_ssi_parse_string(r, ctx, tag_val, NULL, + MAX_STRING_LEN, 0)); + if (val) { + switch(encode) { + case E_NONE: + echo_text = val; + break; + case E_URL: + echo_text = ap_escape_uri(r->pool, val); + break; + case E_ENTITY: + echo_text = ap_escape_html(r->pool, val); + break; + } + + e_len = strlen(echo_text); + tmp_buck = apr_bucket_pool_create(echo_text, e_len, + r->pool, c->bucket_alloc); + } + else { + include_server_config *sconf= + ap_get_module_config(r->server->module_config, + &include_module); + tmp_buck = apr_bucket_pool_create(sconf->undefinedEcho, + sconf->undefinedEchoLen, + r->pool, c->bucket_alloc); + } + APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck); + if (*inserted_head == NULL) { + *inserted_head = tmp_buck; + } + } + else if (!strcmp(tag, "encoding")) { + if (!strcasecmp(tag_val, "none")) encode = E_NONE; + else if (!strcasecmp(tag_val, "url")) encode = E_URL; + else if (!strcasecmp(tag_val, "entity")) encode = E_ENTITY; + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "unknown value \"%s\" to parameter \"encoding\" of " + "tag echo in %s", tag_val, r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, + *inserted_head); + return 1; + } + } + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "unknown parameter \"%s\" in tag echo of %s", + tag, r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); + return 1; + } + + } + } + return 0; +} + +/* error and tf must point to a string with room for at + * least MAX_STRING_LEN characters + */ +static int handle_config(include_ctx_t *ctx, apr_bucket_brigade **bb, + request_rec *r, ap_filter_t *f, apr_bucket *head_ptr, + apr_bucket **inserted_head) +{ + char *tag = NULL; + char *tag_val = NULL; + char *parsed_string; + apr_table_t *env = r->subprocess_env; + + *inserted_head = NULL; + if (ctx->flags & FLAG_PRINTING) { + while (1) { + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 0); + if (tag_val == NULL) { + if (tag == NULL) { + return 0; /* Reached the end of the string. */ + } + else { + return 1; /* tags must have values. */ + } + } + if (!strcmp(tag, "errmsg")) { + if (ctx->error_str_override == NULL) { + ctx->error_str_override = (char *)apr_palloc(ctx->pool, + MAX_STRING_LEN); + ctx->error_str = ctx->error_str_override; + } + ap_ssi_parse_string(r, ctx, tag_val, ctx->error_str_override, + MAX_STRING_LEN, 0); + } + else if (!strcmp(tag, "timefmt")) { + apr_time_t date = r->request_time; + if (ctx->time_str_override == NULL) { + ctx->time_str_override = (char *)apr_palloc(ctx->pool, + MAX_STRING_LEN); + ctx->time_str = ctx->time_str_override; + } + ap_ssi_parse_string(r, ctx, tag_val, ctx->time_str_override, + MAX_STRING_LEN, 0); + apr_table_setn(env, "DATE_LOCAL", ap_ht_time(r->pool, date, + ctx->time_str, 0)); + apr_table_setn(env, "DATE_GMT", ap_ht_time(r->pool, date, + ctx->time_str, 1)); + apr_table_setn(env, "LAST_MODIFIED", + ap_ht_time(r->pool, r->finfo.mtime, + ctx->time_str, 0)); + } + else if (!strcmp(tag, "sizefmt")) { + parsed_string = ap_ssi_parse_string(r, ctx, tag_val, NULL, + MAX_STRING_LEN, 0); + decodehtml(parsed_string); + if (!strcmp(parsed_string, "bytes")) { + ctx->flags |= FLAG_SIZE_IN_BYTES; + } + else if (!strcmp(parsed_string, "abbrev")) { + ctx->flags &= FLAG_SIZE_ABBREV; + } + } + else { + apr_bucket *tmp_buck; + + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "unknown parameter \"%s\" to tag config in %s", + tag, r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); + return 1; + } + } + } + return 0; +} + + +static int find_file(request_rec *r, const char *directive, const char *tag, + char *tag_val, apr_finfo_t *finfo) +{ + char *to_send = tag_val; + request_rec *rr = NULL; + int ret=0; + char *error_fmt = NULL; + apr_status_t rv = APR_SUCCESS; + + if (!strcmp(tag, "file")) { + /* XXX: Port to apr_filepath_merge + * be safe; only files in this directory or below allowed + */ + if (!is_only_below(tag_val)) { + error_fmt = "unable to access file \"%s\" " + "in parsed file %s"; + } + else { + ap_getparents(tag_val); /* get rid of any nasties */ + + /* note: it is okay to pass NULL for the "next filter" since + we never attempt to "run" this sub request. */ + rr = ap_sub_req_lookup_file(tag_val, r, NULL); + + if (rr->status == HTTP_OK && rr->finfo.filetype != 0) { + to_send = rr->filename; + if ((rv = apr_stat(finfo, to_send, + APR_FINFO_GPROT | APR_FINFO_MIN, rr->pool)) != APR_SUCCESS + && rv != APR_INCOMPLETE) { + error_fmt = "unable to get information about \"%s\" " + "in parsed file %s"; + } + } + else { + error_fmt = "unable to lookup information about \"%s\" " + "in parsed file %s"; + } + } + + if (error_fmt) { + ret = -1; + ap_log_rerror(APLOG_MARK, APLOG_ERR, + rv, r, error_fmt, to_send, r->filename); + } + + if (rr) ap_destroy_sub_req(rr); + + return ret; + } + else if (!strcmp(tag, "virtual")) { + /* note: it is okay to pass NULL for the "next filter" since + we never attempt to "run" this sub request. */ + rr = ap_sub_req_lookup_uri(tag_val, r, NULL); + + if (rr->status == HTTP_OK && rr->finfo.filetype != 0) { + memcpy((char *) finfo, (const char *) &rr->finfo, + sizeof(rr->finfo)); + ap_destroy_sub_req(rr); + return 0; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "unable to get information about \"%s\" " + "in parsed file %s", + tag_val, r->filename); + ap_destroy_sub_req(rr); + return -1; + } + } + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "unknown parameter \"%s\" to tag %s in %s", + tag, directive, r->filename); + return -1; + } +} + +static int handle_fsize(include_ctx_t *ctx, apr_bucket_brigade **bb, + request_rec *r, ap_filter_t *f, apr_bucket *head_ptr, + apr_bucket **inserted_head) +{ + char *tag = NULL; + char *tag_val = NULL; + apr_finfo_t finfo; + apr_size_t s_len; + apr_bucket *tmp_buck; + char *parsed_string; + + *inserted_head = NULL; + if (ctx->flags & FLAG_PRINTING) { + while (1) { + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); + if (tag_val == NULL) { + if (tag == NULL) { + return 0; + } + else { + return 1; + } + } + else { + parsed_string = ap_ssi_parse_string(r, ctx, tag_val, NULL, + MAX_STRING_LEN, 0); + if (!find_file(r, "fsize", tag, parsed_string, &finfo)) { + /* XXX: if we *know* we're going to have to copy the + * thing off of the stack anyway, why not palloc buff + * instead of sticking it on the stack; then we can just + * use a pool bucket and skip the copy + */ + char buff[50]; + + if (!(ctx->flags & FLAG_SIZE_IN_BYTES)) { + apr_strfsize(finfo.size, buff); + s_len = strlen (buff); + } + else { + int l, x, pos = 0; + char tmp_buff[50]; + + apr_snprintf(tmp_buff, sizeof(tmp_buff), + "%" APR_OFF_T_FMT, finfo.size); + l = strlen(tmp_buff); /* grrr */ + for (x = 0; x < l; x++) { + if (x && (!((l - x) % 3))) { + buff[pos++] = ','; + } + buff[pos++] = tmp_buff[x]; + } + buff[pos] = '\0'; + s_len = pos; + } + + tmp_buck = apr_bucket_heap_create(buff, s_len, NULL, + r->connection->bucket_alloc); + APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck); + if (*inserted_head == NULL) { + *inserted_head = tmp_buck; + } + } + else { + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, + *inserted_head); + return 1; + } + } + } + } + return 0; +} + +static int handle_flastmod(include_ctx_t *ctx, apr_bucket_brigade **bb, + request_rec *r, ap_filter_t *f, + apr_bucket *head_ptr, apr_bucket **inserted_head) +{ + char *tag = NULL; + char *tag_val = NULL; + apr_finfo_t finfo; + apr_size_t t_len; + apr_bucket *tmp_buck; + char *parsed_string; + + *inserted_head = NULL; + if (ctx->flags & FLAG_PRINTING) { + while (1) { + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); + if (tag_val == NULL) { + if (tag == NULL) { + return 0; + } + else { + return 1; + } + } + else { + parsed_string = ap_ssi_parse_string(r, ctx, tag_val, NULL, + MAX_STRING_LEN, 0); + if (!find_file(r, "flastmod", tag, parsed_string, &finfo)) { + char *t_val; + + t_val = ap_ht_time(r->pool, finfo.mtime, ctx->time_str, 0); + t_len = strlen(t_val); + + tmp_buck = apr_bucket_pool_create(t_val, t_len, r->pool, + r->connection->bucket_alloc); + APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck); + if (*inserted_head == NULL) { + *inserted_head = tmp_buck; + } + } + else { + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, + *inserted_head); + return 1; + } + } + } + } + return 0; +} + +static int re_check(request_rec *r, include_ctx_t *ctx, + char *string, char *rexp) +{ + regex_t *compiled; + const apr_size_t nres = sizeof(*ctx->re_result) / sizeof(regmatch_t); + int regex_error; + + compiled = ap_pregcomp(r->pool, rexp, REG_EXTENDED | REG_NOSUB); + if (compiled == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "unable to compile pattern \"%s\"", rexp); + return -1; + } + if (!ctx->re_result) { + ctx->re_result = apr_pcalloc(r->pool, sizeof(*ctx->re_result)); + } + ctx->re_string = string; + regex_error = ap_regexec(compiled, string, nres, *ctx->re_result, 0); + ap_pregfree(r->pool, compiled); + return (!regex_error); +} + +enum token_type { + token_string, token_re, + token_and, token_or, token_not, token_eq, token_ne, + token_rbrace, token_lbrace, token_group, + token_ge, token_le, token_gt, token_lt +}; +struct token { + enum token_type type; + char* value; +}; + +static const char *get_ptoken(request_rec *r, const char *string, + struct token *token, int *unmatched) +{ + char ch; + int next = 0; + char qs = 0; + int tkn_fnd = 0; + + token->value = NULL; + + /* Skip leading white space */ + if (string == (char *) NULL) { + return (char *) NULL; + } + while ((ch = *string++)) { + if (!apr_isspace(ch)) { + break; + } + } + if (ch == '\0') { + return (char *) NULL; + } + + token->type = token_string; /* the default type */ + switch (ch) { + case '(': + token->type = token_lbrace; + return (string); + case ')': + token->type = token_rbrace; + return (string); + case '=': + token->type = token_eq; + return (string); + case '!': + if (*string == '=') { + token->type = token_ne; + return (string + 1); + } + else { + token->type = token_not; + return (string); + } + case '\'': + /* already token->type == token_string */ + qs = '\''; + break; + case '/': + token->type = token_re; + qs = '/'; + break; + case '|': + if (*string == '|') { + token->type = token_or; + return (string + 1); + } + break; + case '&': + if (*string == '&') { + token->type = token_and; + return (string + 1); + } + break; + case '>': + if (*string == '=') { + token->type = token_ge; + return (string + 1); + } + else { + token->type = token_gt; + return (string); + } + case '<': + if (*string == '=') { + token->type = token_le; + return (string + 1); + } + else { + token->type = token_lt; + return (string); + } + default: + /* already token->type == token_string */ + break; + } + /* We should only be here if we are in a string */ + token->value = apr_palloc(r->pool, strlen(string) + 2); /* 2 for ch plus + trailing null */ + if (!qs) { + --string; + } + + /* + * I used the ++string throughout this section so that string + * ends up pointing to the next token and I can just return it + */ + for (ch = *string; ((ch != '\0') && (!tkn_fnd)); ch = *++string) { + if (ch == '\\') { + if ((ch = *++string) == '\0') { + tkn_fnd = 1; + } + else { + token->value[next++] = ch; + } + } + else { + if (!qs) { + if (apr_isspace(ch)) { + tkn_fnd = 1; + } + else { + switch (ch) { + case '(': + case ')': + case '=': + case '!': + case '<': + case '>': + tkn_fnd = 1; + break; + case '|': + if (*(string + 1) == '|') { + tkn_fnd = 1; + } + break; + case '&': + if (*(string + 1) == '&') { + tkn_fnd = 1; + } + break; + } + if (!tkn_fnd) { + token->value[next++] = ch; + } + } + } + else { + if (ch == qs) { + qs = 0; + tkn_fnd = 1; + string++; + } + else { + token->value[next++] = ch; + } + } + } + if (tkn_fnd) { + break; + } + } + + /* If qs is still set, we have an unmatched quote */ + if (qs) { + *unmatched = 1; + next = 0; + } + token->value[next] = '\0'; + + return (string); +} + + +/* there is an implicit assumption here that expr is at most MAX_STRING_LEN-1 + * characters long... + */ +static int parse_expr(request_rec *r, include_ctx_t *ctx, const char *expr, + int *was_error, int *was_unmatched, char *debug) +{ + struct parse_node { + struct parse_node *left, *right, *parent; + struct token token; + int value, done; + } *root, *current, *new; + const char *parse; + char* buffer; + int retval = 0; + apr_size_t debug_pos = 0; + + debug[debug_pos] = '\0'; + *was_error = 0; + *was_unmatched = 0; + if ((parse = expr) == (char *) NULL) { + return (0); + } + root = current = (struct parse_node *) NULL; + + /* Create Parse Tree */ + while (1) { + new = (struct parse_node *) apr_palloc(r->pool, + sizeof(struct parse_node)); + new->parent = new->left = new->right = (struct parse_node *) NULL; + new->done = 0; + if ((parse = get_ptoken(r, parse, &new->token, was_unmatched)) == + (char *) NULL) { + break; + } + switch (new->token.type) { + + case token_string: +#ifdef DEBUG_INCLUDE + debug_pos += sprintf (&debug[debug_pos], + " Token: string (%s)\n", + new->token.value); +#endif + if (current == (struct parse_node *) NULL) { + root = current = new; + break; + } + switch (current->token.type) { + case token_string: + current->token.value = apr_pstrcat(r->pool, + current->token.value, + current->token.value[0] ? " " : "", + new->token.value, + NULL); + + break; + case token_eq: + case token_ne: + case token_and: + case token_or: + case token_lbrace: + case token_not: + case token_ge: + case token_gt: + case token_le: + case token_lt: + new->parent = current; + current = current->right = new; + break; + default: + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + *was_error = 1; + return retval; + } + break; + + case token_re: +#ifdef DEBUG_INCLUDE + debug_pos += sprintf (&debug[debug_pos], + " Token: regex (%s)\n", + new->token.value); +#endif + if (current == (struct parse_node *) NULL) { + root = current = new; + break; + } + switch (current->token.type) { + case token_eq: + case token_ne: + case token_and: + case token_or: + case token_lbrace: + case token_not: + new->parent = current; + current = current->right = new; + break; + default: + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + *was_error = 1; + return retval; + } + break; + + case token_and: + case token_or: +#ifdef DEBUG_INCLUDE + memcpy (&debug[debug_pos], " Token: and/or\n", + sizeof (" Token: and/or\n")); + debug_pos += sizeof (" Token: and/or\n"); +#endif + if (current == (struct parse_node *) NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + *was_error = 1; + return retval; + } + /* Percolate upwards */ + while (current != (struct parse_node *) NULL) { + switch (current->token.type) { + case token_string: + case token_re: + case token_group: + case token_not: + case token_eq: + case token_ne: + case token_and: + case token_or: + case token_ge: + case token_gt: + case token_le: + case token_lt: + current = current->parent; + continue; + case token_lbrace: + break; + default: + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + *was_error = 1; + return retval; + } + break; + } + if (current == (struct parse_node *) NULL) { + new->left = root; + new->left->parent = new; + new->parent = (struct parse_node *) NULL; + root = new; + } + else { + new->left = current->right; + new->left->parent = new; + current->right = new; + new->parent = current; + } + current = new; + break; + + case token_not: +#ifdef DEBUG_INCLUDE + memcpy(&debug[debug_pos], " Token: not\n", + sizeof(" Token: not\n")); + debug_pos += sizeof(" Token: not\n"); +#endif + if (current == (struct parse_node *) NULL) { + root = current = new; + break; + } + /* Percolate upwards */ + if (current != (struct parse_node *) NULL) { + switch (current->token.type) { + case token_not: + case token_eq: + case token_ne: + case token_and: + case token_or: + case token_lbrace: + case token_ge: + case token_gt: + case token_le: + case token_lt: + break; + default: + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + *was_error = 1; + return retval; + } + } + if (current == (struct parse_node *) NULL) { + new->left = root; + new->left->parent = new; + new->parent = (struct parse_node *) NULL; + root = new; + } + else { + new->left = current->right; + current->right = new; + new->parent = current; + } + current = new; + break; + + case token_eq: + case token_ne: + case token_ge: + case token_gt: + case token_le: + case token_lt: +#ifdef DEBUG_INCLUDE + memcpy(&debug[debug_pos], " Token: eq/ne/ge/gt/le/lt\n", + sizeof(" Token: eq/ne/ge/gt/le/lt\n")); + debug_pos += sizeof(" Token: eq/ne/ge/gt/le/lt\n"); +#endif + if (current == (struct parse_node *) NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + *was_error = 1; + return retval; + } + /* Percolate upwards */ + while (current != (struct parse_node *) NULL) { + switch (current->token.type) { + case token_string: + case token_re: + case token_group: + current = current->parent; + continue; + case token_lbrace: + case token_and: + case token_or: + break; + case token_not: + case token_eq: + case token_ne: + case token_ge: + case token_gt: + case token_le: + case token_lt: + default: + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + *was_error = 1; + return retval; + } + break; + } + if (current == (struct parse_node *) NULL) { + new->left = root; + new->left->parent = new; + new->parent = (struct parse_node *) NULL; + root = new; + } + else { + new->left = current->right; + new->left->parent = new; + current->right = new; + new->parent = current; + } + current = new; + break; + + case token_rbrace: +#ifdef DEBUG_INCLUDE + memcpy (&debug[debug_pos], " Token: rbrace\n", + sizeof (" Token: rbrace\n")); + debug_pos += sizeof (" Token: rbrace\n"); +#endif + while (current != (struct parse_node *) NULL) { + if (current->token.type == token_lbrace) { + current->token.type = token_group; + break; + } + current = current->parent; + } + if (current == (struct parse_node *) NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Unmatched ')' in \"%s\" in file %s", + expr, r->filename); + *was_error = 1; + return retval; + } + break; + + case token_lbrace: +#ifdef DEBUG_INCLUDE + memcpy (&debug[debug_pos], " Token: lbrace\n", + sizeof (" Token: lbrace\n")); + debug_pos += sizeof (" Token: lbrace\n"); +#endif + if (current == (struct parse_node *) NULL) { + root = current = new; + break; + } + /* Percolate upwards */ + if (current != (struct parse_node *) NULL) { + switch (current->token.type) { + case token_not: + case token_eq: + case token_ne: + case token_and: + case token_or: + case token_lbrace: + case token_ge: + case token_gt: + case token_le: + case token_lt: + break; + case token_string: + case token_re: + case token_group: + default: + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + *was_error = 1; + return retval; + } + } + if (current == (struct parse_node *) NULL) { + new->left = root; + new->left->parent = new; + new->parent = (struct parse_node *) NULL; + root = new; + } + else { + new->left = current->right; + current->right = new; + new->parent = current; + } + current = new; + break; + default: + break; + } + } + + /* Evaluate Parse Tree */ + current = root; + while (current != (struct parse_node *) NULL) { + switch (current->token.type) { + case token_string: +#ifdef DEBUG_INCLUDE + memcpy (&debug[debug_pos], " Evaluate string\n", + sizeof (" Evaluate string\n")); + debug_pos += sizeof (" Evaluate string\n"); +#endif + buffer = ap_ssi_parse_string(r, ctx, current->token.value, NULL, + MAX_STRING_LEN, 0); + current->token.value = buffer; + current->value = (current->token.value[0] != '\0'); + current->done = 1; + current = current->parent; + break; + + case token_re: + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "No operator before regex of expr \"%s\" in file %s", + expr, r->filename); + *was_error = 1; + return retval; + + case token_and: + case token_or: +#ifdef DEBUG_INCLUDE + memcpy(&debug[debug_pos], " Evaluate and/or\n", + sizeof(" Evaluate and/or\n")); + debug_pos += sizeof(" Evaluate and/or\n"); +#endif + if (current->left == (struct parse_node *) NULL || + current->right == (struct parse_node *) NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + *was_error = 1; + return retval; + } + if (!current->left->done) { + switch (current->left->token.type) { + case token_string: + buffer = ap_ssi_parse_string(r, ctx, current->left->token.value, + NULL, MAX_STRING_LEN, 0); + current->left->token.value = buffer; + current->left->value = + (current->left->token.value[0] != '\0'); + current->left->done = 1; + break; + default: + current = current->left; + continue; + } + } + if (!current->right->done) { + switch (current->right->token.type) { + case token_string: + buffer = ap_ssi_parse_string(r, ctx, current->right->token.value, + NULL, MAX_STRING_LEN, 0); + current->right->token.value = buffer; + current->right->value = + (current->right->token.value[0] != '\0'); + current->right->done = 1; + break; + default: + current = current->right; + continue; + } + } +#ifdef DEBUG_INCLUDE + debug_pos += sprintf (&debug[debug_pos], " Left: %c\n", + current->left->value ? '1' : '0'); + debug_pos += sprintf (&debug[debug_pos], " Right: %c\n", + current->right->value ? '1' : '0'); +#endif + if (current->token.type == token_and) { + current->value = current->left->value && current->right->value; + } + else { + current->value = current->left->value || current->right->value; + } +#ifdef DEBUG_INCLUDE + debug_pos += sprintf (&debug[debug_pos], " Returning %c\n", + current->value ? '1' : '0'); +#endif + current->done = 1; + current = current->parent; + break; + + case token_eq: + case token_ne: +#ifdef DEBUG_INCLUDE + memcpy (&debug[debug_pos], " Evaluate eq/ne\n", + sizeof (" Evaluate eq/ne\n")); + debug_pos += sizeof (" Evaluate eq/ne\n"); +#endif + if ((current->left == (struct parse_node *) NULL) || + (current->right == (struct parse_node *) NULL) || + (current->left->token.type != token_string) || + ((current->right->token.type != token_string) && + (current->right->token.type != token_re))) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + *was_error = 1; + return retval; + } + buffer = ap_ssi_parse_string(r, ctx, current->left->token.value, + NULL, MAX_STRING_LEN, 0); + current->left->token.value = buffer; + buffer = ap_ssi_parse_string(r, ctx, current->right->token.value, + NULL, MAX_STRING_LEN, 0); + current->right->token.value = buffer; + if (current->right->token.type == token_re) { +#ifdef DEBUG_INCLUDE + debug_pos += sprintf (&debug[debug_pos], + " Re Compare (%s) with /%s/\n", + current->left->token.value, + current->right->token.value); +#endif + current->value = + re_check(r, ctx, current->left->token.value, + current->right->token.value); + } + else { +#ifdef DEBUG_INCLUDE + debug_pos += sprintf (&debug[debug_pos], + " Compare (%s) with (%s)\n", + current->left->token.value, + current->right->token.value); +#endif + current->value = + (strcmp(current->left->token.value, + current->right->token.value) == 0); + } + if (current->token.type == token_ne) { + current->value = !current->value; + } +#ifdef DEBUG_INCLUDE + debug_pos += sprintf (&debug[debug_pos], " Returning %c\n", + current->value ? '1' : '0'); +#endif + current->done = 1; + current = current->parent; + break; + case token_ge: + case token_gt: + case token_le: + case token_lt: +#ifdef DEBUG_INCLUDE + memcpy (&debug[debug_pos], " Evaluate ge/gt/le/lt\n", + sizeof (" Evaluate ge/gt/le/lt\n")); + debug_pos += sizeof (" Evaluate ge/gt/le/lt\n"); +#endif + if ((current->left == (struct parse_node *) NULL) || + (current->right == (struct parse_node *) NULL) || + (current->left->token.type != token_string) || + (current->right->token.type != token_string)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + *was_error = 1; + return retval; + } + buffer = ap_ssi_parse_string(r, ctx, current->left->token.value, + NULL, MAX_STRING_LEN, 0); + current->left->token.value = buffer; + buffer = ap_ssi_parse_string(r, ctx, current->right->token.value, + NULL, MAX_STRING_LEN, 0); + current->right->token.value = buffer; +#ifdef DEBUG_INCLUDE + debug_pos += sprintf (&debug[debug_pos], + " Compare (%s) with (%s)\n", + current->left->token.value, + current->right->token.value); +#endif + current->value = + strcmp(current->left->token.value, + current->right->token.value); + if (current->token.type == token_ge) { + current->value = current->value >= 0; + } + else if (current->token.type == token_gt) { + current->value = current->value > 0; + } + else if (current->token.type == token_le) { + current->value = current->value <= 0; + } + else if (current->token.type == token_lt) { + current->value = current->value < 0; + } + else { + current->value = 0; /* Don't return -1 if unknown token */ + } +#ifdef DEBUG_INCLUDE + debug_pos += sprintf (&debug[debug_pos], " Returning %c\n", + current->value ? '1' : '0'); +#endif + current->done = 1; + current = current->parent; + break; + + case token_not: + if (current->right != (struct parse_node *) NULL) { + if (!current->right->done) { + current = current->right; + continue; + } + current->value = !current->right->value; + } + else { + current->value = 0; + } +#ifdef DEBUG_INCLUDE + debug_pos += sprintf (&debug[debug_pos], " Evaluate !: %c\n", + current->value ? '1' : '0'); +#endif + current->done = 1; + current = current->parent; + break; + + case token_group: + if (current->right != (struct parse_node *) NULL) { + if (!current->right->done) { + current = current->right; + continue; + } + current->value = current->right->value; + } + else { + current->value = 1; + } +#ifdef DEBUG_INCLUDE + debug_pos += sprintf (&debug[debug_pos], " Evaluate (): %c\n", + current->value ? '1' : '0'); +#endif + current->done = 1; + current = current->parent; + break; + + case token_lbrace: + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Unmatched '(' in \"%s\" in file %s", + expr, r->filename); + *was_error = 1; + return retval; + + case token_rbrace: + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Unmatched ')' in \"%s\" in file %s", + expr, r->filename); + *was_error = 1; + return retval; + + default: + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "bad token type"); + *was_error = 1; + return retval; + } + } + + retval = (root == (struct parse_node *) NULL) ? 0 : root->value; + return (retval); +} + +/*-------------------------------------------------------------------------*/ +#ifdef DEBUG_INCLUDE + +#define MAX_DEBUG_SIZE MAX_STRING_LEN +#define LOG_COND_STATUS(cntx, t_buck, h_ptr, ins_head, tag_text) \ +{ \ + char cond_txt[] = "**** X conditional_status=\"0\"\n"; \ + \ + if (cntx->flags & FLAG_COND_TRUE) { \ + cond_txt[31] = '1'; \ + } \ + memcpy(&cond_txt[5], tag_text, sizeof(tag_text)-1); \ + t_buck = apr_bucket_heap_create(cond_txt, sizeof(cond_txt)-1, \ + NULL, h_ptr->list); \ + APR_BUCKET_INSERT_BEFORE(h_ptr, t_buck); \ + \ + if (ins_head == NULL) { \ + ins_head = t_buck; \ + } \ +} +#define DUMP_PARSE_EXPR_DEBUG(t_buck, h_ptr, d_buf, ins_head) \ +{ \ + if (d_buf[0] != '\0') { \ + t_buck = apr_bucket_heap_create(d_buf, strlen(d_buf), \ + NULL, h_ptr->list); \ + APR_BUCKET_INSERT_BEFORE(h_ptr, t_buck); \ + \ + if (ins_head == NULL) { \ + ins_head = t_buck; \ + } \ + } \ +} +#else + +#define MAX_DEBUG_SIZE 10 +#define LOG_COND_STATUS(cntx, t_buck, h_ptr, ins_head, tag_text) +#define DUMP_PARSE_EXPR_DEBUG(t_buck, h_ptr, d_buf, ins_head) + +#endif +/*-------------------------------------------------------------------------*/ + +/* pjr - These seem to allow expr="fred" expr="joe" where joe overwrites fred. */ +static int handle_if(include_ctx_t *ctx, apr_bucket_brigade **bb, + request_rec *r, ap_filter_t *f, apr_bucket *head_ptr, + apr_bucket **inserted_head) +{ + char *tag = NULL; + char *tag_val = NULL; + char *expr = NULL; + int expr_ret, was_error, was_unmatched; + apr_bucket *tmp_buck; + char debug_buf[MAX_DEBUG_SIZE]; + + *inserted_head = NULL; + if (!(ctx->flags & FLAG_PRINTING)) { + ctx->if_nesting_level++; + } + else { + while (1) { + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 0); + if (tag == NULL) { + if (expr == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "missing expr in if statement: %s", + r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, + *inserted_head); + return 1; + } + expr_ret = parse_expr(r, ctx, expr, &was_error, + &was_unmatched, debug_buf); + if (was_error) { + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, + *inserted_head); + return 1; + } + if (was_unmatched) { + DUMP_PARSE_EXPR_DEBUG(tmp_buck, head_ptr, + "\nUnmatched '\n", *inserted_head); + } + DUMP_PARSE_EXPR_DEBUG(tmp_buck, head_ptr, debug_buf, + *inserted_head); + + if (expr_ret) { + ctx->flags |= (FLAG_PRINTING | FLAG_COND_TRUE); + } + else { + ctx->flags &= FLAG_CLEAR_PRINT_COND; + } + LOG_COND_STATUS(ctx, tmp_buck, head_ptr, *inserted_head, + " if"); + ctx->if_nesting_level = 0; + return 0; + } + else if (!strcmp(tag, "expr")) { + expr = tag_val; +#ifdef DEBUG_INCLUDE + if (1) { + apr_size_t d_len = 0; + d_len = sprintf(debug_buf, "**** if expr=\"%s\"\n", expr); + tmp_buck = apr_bucket_heap_create(debug_buf, d_len, NULL, + r->connection->bucket_alloc); + APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck); + + if (*inserted_head == NULL) { + *inserted_head = tmp_buck; + } + } +#endif + } + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "unknown parameter \"%s\" to tag if in %s", tag, + r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); + return 1; + } + + } + } + return 0; +} + +static int handle_elif(include_ctx_t *ctx, apr_bucket_brigade **bb, + request_rec *r, ap_filter_t *f, apr_bucket *head_ptr, + apr_bucket **inserted_head) +{ + char *tag = NULL; + char *tag_val = NULL; + char *expr = NULL; + int expr_ret, was_error, was_unmatched; + apr_bucket *tmp_buck; + char debug_buf[MAX_DEBUG_SIZE]; + + *inserted_head = NULL; + if (!ctx->if_nesting_level) { + while (1) { + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 0); + if (tag == '\0') { + LOG_COND_STATUS(ctx, tmp_buck, head_ptr, *inserted_head, + " elif"); + + if (ctx->flags & FLAG_COND_TRUE) { + ctx->flags &= FLAG_CLEAR_PRINTING; + return (0); + } + if (expr == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "missing expr in elif statement: %s", + r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, + *inserted_head); + return 1; + } + expr_ret = parse_expr(r, ctx, expr, &was_error, + &was_unmatched, debug_buf); + if (was_error) { + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, + *inserted_head); + return 1; + } + if (was_unmatched) { + DUMP_PARSE_EXPR_DEBUG(tmp_buck, head_ptr, + "\nUnmatched '\n", *inserted_head); + } + DUMP_PARSE_EXPR_DEBUG(tmp_buck, head_ptr, debug_buf, + *inserted_head); + + if (expr_ret) { + ctx->flags |= (FLAG_PRINTING | FLAG_COND_TRUE); + } + else { + ctx->flags &= FLAG_CLEAR_PRINT_COND; + } + LOG_COND_STATUS(ctx, tmp_buck, head_ptr, *inserted_head, + " elif"); + return (0); + } + else if (!strcmp(tag, "expr")) { + expr = tag_val; +#ifdef DEBUG_INCLUDE + if (1) { + apr_size_t d_len = 0; + d_len = sprintf(debug_buf, "**** elif expr=\"%s\"\n", expr); + tmp_buck = apr_bucket_heap_create(debug_buf, d_len, NULL, + r->connection->bucket_alloc); + APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck); + + if (*inserted_head == NULL) { + *inserted_head = tmp_buck; + } + } +#endif + } + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "unknown parameter \"%s\" to tag if in %s", tag, + r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); + return 1; + } + } + } + return 0; +} + +static int handle_else(include_ctx_t *ctx, apr_bucket_brigade **bb, + request_rec *r, ap_filter_t *f, apr_bucket *head_ptr, + apr_bucket **inserted_head) +{ + char *tag = NULL; + char *tag_val = NULL; + apr_bucket *tmp_buck; + + *inserted_head = NULL; + if (!ctx->if_nesting_level) { + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); + if ((tag != NULL) || (tag_val != NULL)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "else directive does not take tags in %s", r->filename); + if (ctx->flags & FLAG_PRINTING) { + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); + } + return -1; + } + else { + LOG_COND_STATUS(ctx, tmp_buck, head_ptr, *inserted_head, " else"); + + if (ctx->flags & FLAG_COND_TRUE) { + ctx->flags &= FLAG_CLEAR_PRINTING; + } + else { + ctx->flags |= (FLAG_PRINTING | FLAG_COND_TRUE); + } + return 0; + } + } + return 0; +} + +static int handle_endif(include_ctx_t *ctx, apr_bucket_brigade **bb, + request_rec *r, ap_filter_t *f, apr_bucket *head_ptr, + apr_bucket **inserted_head) +{ + char *tag = NULL; + char *tag_val = NULL; + apr_bucket *tmp_buck; + + *inserted_head = NULL; + if (!ctx->if_nesting_level) { + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); + if ((tag != NULL) || (tag_val != NULL)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "endif directive does not take tags in %s", r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); + return -1; + } + else { + LOG_COND_STATUS(ctx, tmp_buck, head_ptr, *inserted_head, "endif"); + ctx->flags |= (FLAG_PRINTING | FLAG_COND_TRUE); + return 0; + } + } + else { + ctx->if_nesting_level--; + return 0; + } +} + +static int handle_set(include_ctx_t *ctx, apr_bucket_brigade **bb, + request_rec *r, ap_filter_t *f, apr_bucket *head_ptr, + apr_bucket **inserted_head) +{ + char *tag = NULL; + char *tag_val = NULL; + char *var = NULL; + apr_bucket *tmp_buck; + char *parsed_string; + request_rec *sub = r->main; + apr_pool_t *p = r->pool; + + /* we need to use the 'main' request pool to set notes as that is + * a notes lifetime + */ + while (sub) { + p = sub->pool; + sub = sub->main; + } + + *inserted_head = NULL; + if (ctx->flags & FLAG_PRINTING) { + while (1) { + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); + if ((tag == NULL) && (tag_val == NULL)) { + return 0; + } + else if (tag_val == NULL) { + return 1; + } + else if (!strcmp(tag, "var")) { + var = ap_ssi_parse_string(r, ctx, tag_val, NULL, + MAX_STRING_LEN, 0); + } + else if (!strcmp(tag, "value")) { + if (var == (char *) NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "variable must precede value in set directive in %s", + r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, + *inserted_head); + return (-1); + } + parsed_string = ap_ssi_parse_string(r, ctx, tag_val, NULL, + MAX_STRING_LEN, 0); + apr_table_setn(r->subprocess_env, apr_pstrdup(p, var), + apr_pstrdup(p, parsed_string)); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Invalid tag for set directive in %s", r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); + return -1; + } + } + } + return 0; +} + +static int handle_printenv(include_ctx_t *ctx, apr_bucket_brigade **bb, + request_rec *r, ap_filter_t *f, + apr_bucket *head_ptr, apr_bucket **inserted_head) +{ + char *tag = NULL; + char *tag_val = NULL; + apr_bucket *tmp_buck; + + if (ctx->flags & FLAG_PRINTING) { + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); + if ((tag == NULL) && (tag_val == NULL)) { + const apr_array_header_t *arr = apr_table_elts(r->subprocess_env); + const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts; + int i; + const char *key_text, *val_text; + char *key_val, *next; + apr_size_t k_len, v_len, kv_length; + + *inserted_head = NULL; + for (i = 0; i < arr->nelts; ++i) { + key_text = ap_escape_html(r->pool, elts[i].key); + val_text = elts[i].val; + if (val_text == LAZY_VALUE) { + val_text = add_include_vars_lazy(r, elts[i].key); + } + val_text = ap_escape_html(r->pool, elts[i].val); + k_len = strlen(key_text); + v_len = strlen(val_text); + kv_length = k_len + v_len + sizeof("=\n"); + key_val = apr_palloc(r->pool, kv_length); + next = key_val; + memcpy(next, key_text, k_len); + next += k_len; + *next++ = '='; + memcpy(next, val_text, v_len); + next += v_len; + *next++ = '\n'; + *next = 0; + tmp_buck = apr_bucket_pool_create(key_val, kv_length - 1, + r->pool, + r->connection->bucket_alloc); + APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck); + if (*inserted_head == NULL) { + *inserted_head = tmp_buck; + } + } + return 0; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "printenv directive does not take tags in %s", + r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); + return -1; + } + } + return 0; +} + +/* -------------------------- The main function --------------------------- */ + +/* + * returns the index position of the first byte of start_seq (or the len of + * the buffer as non-match) + */ +static apr_size_t find_start_sequence(ssi_ctx_t *ctx, const char *data, + apr_size_t len) +{ + apr_size_t slen = ctx->ctx->start_seq_len; + apr_size_t index; + const char *p, *ep; + + if (len < slen) { + p = data; /* try partial match at the end of the buffer (below) */ + } + else { + /* try fast bndm search over the buffer + * (hopefully the whole start sequence can be found in this buffer) + */ + index = bndm(ctx->ctx->start_seq, ctx->ctx->start_seq_len, data, len, + ctx->ctx->start_seq_pat); + + /* wow, found it. ready. */ + if (index < len) { + ctx->state = PARSE_DIRECTIVE; + return index; + } + else { + /* ok, the pattern can't be found as whole in the buffer, + * check the end for a partial match + */ + p = data + len - slen + 1; + } + } + + ep = data + len; + do { + while (p < ep && *p != *ctx->ctx->start_seq) { + ++p; + } + + index = p - data; + + /* found a possible start_seq start */ + if (p < ep) { + apr_size_t pos = 1; + + ++p; + while (p < ep && *p == ctx->ctx->start_seq[pos]) { + ++p; + ++pos; + } + + /* partial match found. Store the info for the next round */ + if (p == ep) { + ctx->state = PARSE_HEAD; + ctx->ctx->parse_pos = pos; + return index; + } + } + + /* we must try all combinations; consider (e.g.) SSIStartTag "--->" + * and a string data of "--.-" and the end of the buffer + */ + p = data + index + 1; + } while (p < ep); + + /* no match */ + return len; +} + +/* + * returns the first byte *after* the partial (or final) match. + * + * If we had to trick with the start_seq start, 'release' returns the + * number of chars of the start_seq which appeared not to be part of a + * full tag and may have to be passed down the filter chain. + */ +static apr_size_t find_partial_start_sequence(ssi_ctx_t *ctx, + const char *data, + apr_size_t len, + apr_size_t *release) +{ + apr_size_t pos, spos = 0; + apr_size_t slen = ctx->ctx->start_seq_len; + const char *p, *ep; + + pos = ctx->ctx->parse_pos; + ep = data + len; + *release = 0; + + do { + p = data; + + while (p < ep && pos < slen && *p == ctx->ctx->start_seq[pos]) { + ++p; + ++pos; + } + + /* full match */ + if (pos == slen) { + ctx->state = PARSE_DIRECTIVE; + return (p - data); + } + + /* the whole buffer is a partial match */ + if (p == ep) { + ctx->ctx->parse_pos = pos; + return (p - data); + } + + /* No match so far, but again: + * We must try all combinations, since the start_seq is a random + * user supplied string + * + * So: look if the first char of start_seq appears somewhere within + * the current partial match. If it does, try to start a match that + * begins with this offset. (This can happen, if a strange + * start_seq like "---->" spans buffers) + */ + if (spos < ctx->ctx->parse_pos) { + do { + ++spos; + ++*release; + p = ctx->ctx->start_seq + spos; + pos = ctx->ctx->parse_pos - spos; + + while (pos && *p != *ctx->ctx->start_seq) { + ++p; + ++spos; + ++*release; + --pos; + } + + /* if a matching beginning char was found, try to match the + * remainder of the old buffer. + */ + if (pos > 1) { + apr_size_t t = 1; + + ++p; + while (t < pos && *p == ctx->ctx->start_seq[t]) { + ++p; + ++t; + } + + if (t == pos) { + /* yeah, another partial match found in the *old* + * buffer, now test the *current* buffer for + * continuing match + */ + break; + } + } + } while (pos > 1); + + if (pos) { + continue; + } + } + + break; + } while (1); /* work hard to find a match ;-) */ + + /* no match at all, release all (wrongly) matched chars so far */ + *release = ctx->ctx->parse_pos; + ctx->state = PARSE_PRE_HEAD; + return 0; +} + +/* + * returns the position after the directive + */ +static apr_size_t find_directive(ssi_ctx_t *ctx, const char *data, + apr_size_t len, char ***store, + apr_size_t **store_len) +{ + const char *p = data; + const char *ep = data + len; + apr_size_t pos; + + switch (ctx->state) { + case PARSE_DIRECTIVE: + while (p < ep && !apr_isspace(*p)) { + /* we have to consider the case of missing space between directive + * and end_seq (be somewhat lenient), e.g. <!--#printenv--> + */ + if (*p == *ctx->ctx->end_seq) { + ctx->state = PARSE_DIRECTIVE_TAIL; + ctx->ctx->parse_pos = 1; + ++p; + return (p - data); + } + ++p; + } + + if (p < ep) { /* found delimiter whitespace */ + ctx->state = PARSE_DIRECTIVE_POSTNAME; + *store = &ctx->directive; + *store_len = &ctx->ctx->directive_length; + } + + break; + + case PARSE_DIRECTIVE_TAIL: + pos = ctx->ctx->parse_pos; + + while (p < ep && pos < ctx->end_seq_len && + *p == ctx->ctx->end_seq[pos]) { + ++p; + ++pos; + } + + /* full match, we're done */ + if (pos == ctx->end_seq_len) { + ctx->state = PARSE_DIRECTIVE_POSTTAIL; + *store = &ctx->directive; + *store_len = &ctx->ctx->directive_length; + break; + } + + /* partial match, the buffer is too small to match fully */ + if (p == ep) { + ctx->ctx->parse_pos = pos; + break; + } + + /* no match. continue normal parsing */ + ctx->state = PARSE_DIRECTIVE; + return 0; + + case PARSE_DIRECTIVE_POSTTAIL: + ctx->state = PARSE_EXECUTE; + ctx->ctx->directive_length -= ctx->end_seq_len; + /* continue immediately with the next state */ + + case PARSE_DIRECTIVE_POSTNAME: + if (PARSE_DIRECTIVE_POSTNAME == ctx->state) { + ctx->state = PARSE_PRE_ARG; + } + ctx->argc = 0; + ctx->argv = NULL; + + if (!ctx->ctx->directive_length) { + ctx->error = 1; + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "missing directive " + "name in parsed document %s", ctx->r->filename); + } + else { + char *sp = ctx->directive; + char *sep = ctx->directive + ctx->ctx->directive_length; + + /* normalize directive name */ + for (; sp < sep; ++sp) { + *sp = apr_tolower(*sp); + } + } + + return 0; + + default: + /* get a rid of a gcc warning about unhandled enumerations */ + break; + } + + return (p - data); +} + +/* + * find out whether the next token is (a possible) end_seq or an argument + */ +static apr_size_t find_arg_or_tail(ssi_ctx_t *ctx, const char *data, + apr_size_t len) +{ + const char *p = data; + const char *ep = data + len; + + /* skip leading WS */ + while (p < ep && apr_isspace(*p)) { + ++p; + } + + /* buffer doesn't consist of whitespaces only */ + if (p < ep) { + ctx->state = (*p == *ctx->ctx->end_seq) ? PARSE_TAIL : PARSE_ARG; + } + + return (p - data); +} + +/* + * test the stream for end_seq. If it doesn't match at all, it must be an + * argument + */ +static apr_size_t find_tail(ssi_ctx_t *ctx, const char *data, + apr_size_t len) +{ + const char *p = data; + const char *ep = data + len; + apr_size_t pos = ctx->ctx->parse_pos; + + if (PARSE_TAIL == ctx->state) { + ctx->state = PARSE_TAIL_SEQ; + pos = ctx->ctx->parse_pos = 0; + } + + while (p < ep && pos < ctx->end_seq_len && *p == ctx->ctx->end_seq[pos]) { + ++p; + ++pos; + } + + /* bingo, full match */ + if (pos == ctx->end_seq_len) { + ctx->state = PARSE_EXECUTE; + return (p - data); + } + + /* partial match, the buffer is too small to match fully */ + if (p == ep) { + ctx->ctx->parse_pos = pos; + return (p - data); + } + + /* no match. It must be an argument string then */ + ctx->state = PARSE_ARG; + return 0; +} + +/* + * extract name=value from the buffer + * A pcre-pattern could look (similar to): + * name\s*(?:=\s*(["'`]?)value\1(?>\s*))? + */ +static apr_size_t find_argument(ssi_ctx_t *ctx, const char *data, + apr_size_t len, char ***store, + apr_size_t **store_len) +{ + const char *p = data; + const char *ep = data + len; + + switch (ctx->state) { + case PARSE_ARG: + /* + * create argument structure and append it to the current list + */ + ctx->current_arg = apr_palloc(ctx->dpool, + sizeof(*ctx->current_arg)); + ctx->current_arg->next = NULL; + + ++(ctx->argc); + if (!ctx->argv) { + ctx->argv = ctx->current_arg; + } + else { + ssi_arg_item_t *newarg = ctx->argv; + + while (newarg->next) { + newarg = newarg->next; + } + newarg->next = ctx->current_arg; + } + + /* check whether it's a valid one. If it begins with a quote, we + * can safely assume, someone forgot the name of the argument + */ + switch (*p) { + case '"': case '\'': case '`': + *store = NULL; + + ctx->state = PARSE_ARG_VAL; + ctx->quote = *p++; + ctx->current_arg->name = NULL; + ctx->current_arg->name_len = 0; + ctx->error = 1; + + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "missing argument " + "name for value to tag %s in %s", + apr_pstrmemdup(ctx->r->pool, ctx->directive, + ctx->ctx->directive_length), + ctx->r->filename); + + return (p - data); + + default: + ctx->state = PARSE_ARG_NAME; + } + /* continue immediately with next state */ + + case PARSE_ARG_NAME: + while (p < ep && !apr_isspace(*p) && *p != '=') { + ++p; + } + + if (p < ep) { + ctx->state = PARSE_ARG_POSTNAME; + *store = &ctx->current_arg->name; + *store_len = &ctx->current_arg->name_len; + return (p - data); + } + break; + + case PARSE_ARG_POSTNAME: + ctx->current_arg->name = apr_pstrmemdup(ctx->dpool, + ctx->current_arg->name, + ctx->current_arg->name_len); + if (!ctx->current_arg->name_len) { + ctx->error = 1; + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "missing argument " + "name for value to tag %s in %s", + apr_pstrmemdup(ctx->r->pool, ctx->directive, + ctx->ctx->directive_length), + ctx->r->filename); + } + else { + char *sp = ctx->current_arg->name; + + /* normalize the name */ + while (*sp) { + *sp = apr_tolower(*sp); + ++sp; + } + } + + ctx->state = PARSE_ARG_EQ; + /* continue with next state immediately */ + + case PARSE_ARG_EQ: + *store = NULL; + + while (p < ep && apr_isspace(*p)) { + ++p; + } + + if (p < ep) { + if (*p == '=') { + ctx->state = PARSE_ARG_PREVAL; + ++p; + } + else { /* no value */ + ctx->current_arg->value = NULL; + ctx->state = PARSE_PRE_ARG; + } + + return (p - data); + } + break; + + case PARSE_ARG_PREVAL: + *store = NULL; + + while (p < ep && apr_isspace(*p)) { + ++p; + } + + /* buffer doesn't consist of whitespaces only */ + if (p < ep) { + ctx->state = PARSE_ARG_VAL; + switch (*p) { + case '"': case '\'': case '`': + ctx->quote = *p++; + break; + default: + ctx->quote = '\0'; + break; + } + + return (p - data); + } + break; + + case PARSE_ARG_VAL_ESC: + if (*p == ctx->quote) { + ++p; + } + ctx->state = PARSE_ARG_VAL; + /* continue with next state immediately */ + + case PARSE_ARG_VAL: + for (; p < ep; ++p) { + if (ctx->quote && *p == '\\') { + ++p; + if (p == ep) { + ctx->state = PARSE_ARG_VAL_ESC; + break; + } + + if (*p != ctx->quote) { + --p; + } + } + else if (ctx->quote && *p == ctx->quote) { + ++p; + *store = &ctx->current_arg->value; + *store_len = &ctx->current_arg->value_len; + ctx->state = PARSE_ARG_POSTVAL; + break; + } + else if (!ctx->quote && apr_isspace(*p)) { + ++p; + *store = &ctx->current_arg->value; + *store_len = &ctx->current_arg->value_len; + ctx->state = PARSE_ARG_POSTVAL; + break; + } + } + + return (p - data); + + case PARSE_ARG_POSTVAL: + /* + * The value is still the raw input string. Finally clean it up. + */ + --(ctx->current_arg->value_len); + + /* strip quote escaping \ from the string */ + if (ctx->quote) { + apr_size_t shift = 0; + char *sp; + + sp = ctx->current_arg->value; + ep = ctx->current_arg->value + ctx->current_arg->value_len; + while (sp < ep && *sp != '\\') { + ++sp; + } + for (; sp < ep; ++sp) { + if (*sp == '\\' && sp[1] == ctx->quote) { + ++sp; + ++shift; + } + if (shift) { + *(sp-shift) = *sp; + } + } + + ctx->current_arg->value_len -= shift; + } + + ctx->current_arg->value[ctx->current_arg->value_len] = '\0'; + ctx->state = PARSE_PRE_ARG; + + return 0; + + default: + /* get a rid of a gcc warning about unhandled enumerations */ + break; + } + + return len; /* partial match of something */ +} + +/* + * This is the main loop over the current bucket brigade. + */ +static apr_status_t send_parsed_content(ap_filter_t *f, apr_bucket_brigade *bb) +{ + ssi_ctx_t *ctx = f->ctx; + request_rec *r = f->r; + apr_bucket *b = APR_BRIGADE_FIRST(bb); + apr_bucket_brigade *pass_bb; + apr_status_t rv = APR_SUCCESS; + char *magic; /* magic pointer for sentinel use */ + + /* fast exit */ + if (APR_BRIGADE_EMPTY(bb)) { + return APR_SUCCESS; + } + + /* we may crash, since already cleaned up; hand over the responsibility + * to the next filter;-) + */ + if (ctx->seen_eos) { + return ap_pass_brigade(f->next, bb); + } + + /* All stuff passed along has to be put into that brigade */ + pass_bb = apr_brigade_create(ctx->ctx->pool, f->c->bucket_alloc); + ctx->ctx->bytes_parsed = 0; + ctx->ctx->output_now = 0; + ctx->error = 0; + + /* loop over the current bucket brigade */ + while (b != APR_BRIGADE_SENTINEL(bb)) { + const char *data = NULL; + apr_size_t len, index, release; + apr_bucket *newb = NULL; + char **store = &magic; + apr_size_t *store_len; + + /* handle meta buckets before reading any data */ + if (APR_BUCKET_IS_METADATA(b)) { + newb = APR_BUCKET_NEXT(b); + + APR_BUCKET_REMOVE(b); + + if (APR_BUCKET_IS_EOS(b)) { + ctx->seen_eos = 1; + + /* Hit end of stream, time for cleanup ... But wait! + * Perhaps we're not ready yet. We may have to loop one or + * two times again to finish our work. In that case, we + * just re-insert the EOS bucket to allow for an extra loop. + * + * PARSE_EXECUTE means, we've hit a directive just before the + * EOS, which is now waiting for execution. + * + * PARSE_DIRECTIVE_POSTTAIL means, we've hit a directive with + * no argument and no space between directive and end_seq + * just before the EOS. (consider <!--#printenv--> as last + * or only string within the stream). This state, however, + * just cleans up and turns itself to PARSE_EXECUTE, which + * will be passed through within the next (and actually + * last) round. + */ + if (PARSE_EXECUTE == ctx->state || + PARSE_DIRECTIVE_POSTTAIL == ctx->state) { + APR_BUCKET_INSERT_BEFORE(newb, b); + } + else { + break; /* END OF STREAM */ + } + } + else { + APR_BRIGADE_INSERT_TAIL(pass_bb, b); + + if (APR_BUCKET_IS_FLUSH(b)) { + ctx->ctx->output_now = 1; + } + + b = newb; + continue; + } + } + + /* enough is enough ... */ + if (ctx->ctx->output_now || + ctx->ctx->bytes_parsed > AP_MIN_BYTES_TO_WRITE) { + + if (!APR_BRIGADE_EMPTY(pass_bb)) { + rv = ap_pass_brigade(f->next, pass_bb); + if (!APR_STATUS_IS_SUCCESS(rv)) { + apr_brigade_destroy(pass_bb); + return rv; + } + } + + ctx->ctx->output_now = 0; + ctx->ctx->bytes_parsed = 0; + } + + /* read the current bucket data */ + len = 0; + if (!ctx->seen_eos) { + if (ctx->ctx->bytes_parsed > 0) { + rv = apr_bucket_read(b, &data, &len, APR_NONBLOCK_READ); + if (APR_STATUS_IS_EAGAIN(rv)) { + ctx->ctx->output_now = 1; + continue; + } + } + + if (!len || !APR_STATUS_IS_SUCCESS(rv)) { + rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ); + } + + if (!APR_STATUS_IS_SUCCESS(rv)) { + apr_brigade_destroy(pass_bb); + return rv; + } + + ctx->ctx->bytes_parsed += len; + } + + /* zero length bucket, fetch next one */ + if (!len && !ctx->seen_eos) { + b = APR_BUCKET_NEXT(b); + continue; + } + + /* + * it's actually a data containing bucket, start/continue parsing + */ + + switch (ctx->state) { + /* no current tag; search for start sequence */ + case PARSE_PRE_HEAD: + index = find_start_sequence(ctx, data, len); + + if (index < len) { + apr_bucket_split(b, index); + } + + newb = APR_BUCKET_NEXT(b); + if (ctx->ctx->flags & FLAG_PRINTING) { + APR_BUCKET_REMOVE(b); + APR_BRIGADE_INSERT_TAIL(pass_bb, b); + } + else { + apr_bucket_delete(b); + } + + if (index < len) { + /* now delete the start_seq stuff from the remaining bucket */ + if (PARSE_DIRECTIVE == ctx->state) { /* full match */ + apr_bucket_split(newb, ctx->ctx->start_seq_len); + ctx->ctx->output_now = 1; /* pass pre-tag stuff */ + } + + b = APR_BUCKET_NEXT(newb); + apr_bucket_delete(newb); + } + else { + b = newb; + } + + break; + + /* we're currently looking for the end of the start sequence */ + case PARSE_HEAD: + index = find_partial_start_sequence(ctx, data, len, &release); + + /* check if we mismatched earlier and have to release some chars */ + if (release && (ctx->ctx->flags & FLAG_PRINTING)) { + char *to_release = apr_palloc(ctx->ctx->pool, release); + + memcpy(to_release, ctx->ctx->start_seq, release); + newb = apr_bucket_pool_create(to_release, release, + ctx->ctx->pool, + f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(pass_bb, newb); + } + + if (index) { /* any match */ + /* now delete the start_seq stuff from the remaining bucket */ + if (PARSE_DIRECTIVE == ctx->state) { /* final match */ + apr_bucket_split(b, index); + ctx->ctx->output_now = 1; /* pass pre-tag stuff */ + } + newb = APR_BUCKET_NEXT(b); + apr_bucket_delete(b); + b = newb; + } + + break; + + /* we're currently grabbing the directive name */ + case PARSE_DIRECTIVE: + case PARSE_DIRECTIVE_POSTNAME: + case PARSE_DIRECTIVE_TAIL: + case PARSE_DIRECTIVE_POSTTAIL: + index = find_directive(ctx, data, len, &store, &store_len); + + if (index) { + apr_bucket_split(b, index); + newb = APR_BUCKET_NEXT(b); + } + + if (store) { + if (index) { + APR_BUCKET_REMOVE(b); + APR_BRIGADE_INSERT_TAIL(ctx->tmp_bb, b); + b = newb; + } + + /* time for cleanup? */ + if (store != &magic) { + apr_brigade_pflatten(ctx->tmp_bb, store, store_len, + ctx->dpool); + apr_brigade_cleanup(ctx->tmp_bb); + } + } + else if (index) { + apr_bucket_delete(b); + b = newb; + } + + break; + + /* skip WS and find out what comes next (arg or end_seq) */ + case PARSE_PRE_ARG: + index = find_arg_or_tail(ctx, data, len); + + if (index) { /* skipped whitespaces */ + if (index < len) { + apr_bucket_split(b, index); + } + newb = APR_BUCKET_NEXT(b); + apr_bucket_delete(b); + b = newb; + } + + break; + + /* currently parsing name[=val] */ + case PARSE_ARG: + case PARSE_ARG_NAME: + case PARSE_ARG_POSTNAME: + case PARSE_ARG_EQ: + case PARSE_ARG_PREVAL: + case PARSE_ARG_VAL: + case PARSE_ARG_VAL_ESC: + case PARSE_ARG_POSTVAL: + index = find_argument(ctx, data, len, &store, &store_len); + + if (index) { + apr_bucket_split(b, index); + newb = APR_BUCKET_NEXT(b); + } + + if (store) { + if (index) { + APR_BUCKET_REMOVE(b); + APR_BRIGADE_INSERT_TAIL(ctx->tmp_bb, b); + b = newb; + } + + /* time for cleanup? */ + if (store != &magic) { + apr_brigade_pflatten(ctx->tmp_bb, store, store_len, + ctx->dpool); + apr_brigade_cleanup(ctx->tmp_bb); + } + } + else if (index) { + apr_bucket_delete(b); + b = newb; + } + + break; + + /* try to match end_seq at current pos. */ + case PARSE_TAIL: + case PARSE_TAIL_SEQ: + index = find_tail(ctx, data, len); + + switch (ctx->state) { + case PARSE_EXECUTE: /* full match */ + apr_bucket_split(b, index); + newb = APR_BUCKET_NEXT(b); + apr_bucket_delete(b); + b = newb; + break; + + case PARSE_ARG: /* no match */ + /* PARSE_ARG must reparse at the beginning */ + APR_BRIGADE_PREPEND(bb, ctx->tmp_bb); + b = APR_BRIGADE_FIRST(bb); + break; + + default: /* partial match */ + newb = APR_BUCKET_NEXT(b); + APR_BUCKET_REMOVE(b); + APR_BRIGADE_INSERT_TAIL(ctx->tmp_bb, b); + b = newb; + break; + } + + break; + + /* now execute the parsed directive, cleanup the space and + * start again with PARSE_PRE_HEAD + */ + case PARSE_EXECUTE: + /* if there was an error, it was already logged; just stop here */ + if (ctx->error) { + if (ctx->ctx->flags & FLAG_PRINTING) { + SSI_CREATE_ERROR_BUCKET(ctx->ctx, f, pass_bb); + ctx->error = 0; + } + } + else { + include_handler_fn_t *handle_func; + + handle_func = + (include_handler_fn_t *) apr_hash_get(include_hash, + ctx->directive, + ctx->ctx->directive_length); + if (handle_func) { + apr_bucket *dummy; + char *tag; + apr_size_t tag_len = 0; + ssi_arg_item_t *carg = ctx->argv; + + /* legacy wrapper code */ + while (carg) { + /* +1 \0 byte (either after tag or value) + * +1 = byte (before value) + */ + tag_len += (carg->name ? carg->name_len : 0) + + (carg->value ? carg->value_len + 1 : 0) + 1; + carg = carg->next; + } + + tag = ctx->ctx->combined_tag = ctx->ctx->curr_tag_pos = + apr_palloc(ctx->dpool, tag_len); + + carg = ctx->argv; + while (carg) { + if (carg->name) { + memcpy(tag, carg->name, carg->name_len); + tag += carg->name_len; + } + if (carg->value) { + *tag++ = '='; + memcpy(tag, carg->value, carg->value_len); + tag += carg->value_len; + } + *tag++ = '\0'; + carg = carg->next; + } + ctx->ctx->tag_length = tag_len; + + /* create dummy buckets for backards compat */ + ctx->ctx->head_start_bucket = + apr_bucket_pool_create(apr_pmemdup(ctx->ctx->pool, + ctx->ctx->start_seq, + ctx->ctx->start_seq_len), + ctx->ctx->start_seq_len, + ctx->ctx->pool, + f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(ctx->ctx->ssi_tag_brigade, + ctx->ctx->head_start_bucket); + ctx->ctx->tag_start_bucket = + apr_bucket_pool_create(apr_pmemdup(ctx->ctx->pool, + ctx->ctx->combined_tag, + ctx->ctx->tag_length), + ctx->ctx->tag_length, + ctx->ctx->pool, + f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(ctx->ctx->ssi_tag_brigade, + ctx->ctx->tag_start_bucket); + ctx->ctx->tail_start_bucket = + apr_bucket_pool_create(apr_pmemdup(ctx->ctx->pool, + ctx->ctx->end_seq, + ctx->end_seq_len), + ctx->end_seq_len, + ctx->ctx->pool, + f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(ctx->ctx->ssi_tag_brigade, + ctx->ctx->tail_start_bucket); + + rv = handle_func(ctx->ctx, &bb, r, f, b, &dummy); + + apr_brigade_cleanup(ctx->ctx->ssi_tag_brigade); + + if (rv != 0 && rv != 1 && rv != -1) { + apr_brigade_destroy(pass_bb); + return rv; + } + + if (dummy) { + apr_bucket_brigade *remain; + + remain = apr_brigade_split(bb, b); + APR_BRIGADE_CONCAT(pass_bb, bb); + bb = remain; + } + } + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "unknown directive \"%s\" in parsed doc %s", + apr_pstrmemdup(r->pool, ctx->directive, + ctx->ctx->directive_length), + r->filename); + if (ctx->ctx->flags & FLAG_PRINTING) { + SSI_CREATE_ERROR_BUCKET(ctx->ctx, f, pass_bb); + } + } + } + + /* cleanup */ + apr_pool_clear(ctx->dpool); + apr_brigade_cleanup(ctx->tmp_bb); + + /* Oooof. Done here, start next round */ + ctx->state = PARSE_PRE_HEAD; + break; + } + + } /* while (brigade) */ + + /* End of stream. Final cleanup */ + if (ctx->seen_eos) { + if (PARSE_HEAD == ctx->state) { + if (ctx->ctx->flags & FLAG_PRINTING) { + char *to_release = apr_palloc(ctx->ctx->pool, + ctx->ctx->parse_pos); + + memcpy(to_release, ctx->ctx->start_seq, ctx->ctx->parse_pos); + APR_BRIGADE_INSERT_TAIL(pass_bb, + apr_bucket_pool_create(to_release, + ctx->ctx->parse_pos, ctx->ctx->pool, + f->c->bucket_alloc)); + } + } + else if (PARSE_PRE_HEAD != ctx->state) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "SSI directive was not properly finished at the end " + "of parsed document %s", r->filename); + if (ctx->ctx->flags & FLAG_PRINTING) { + SSI_CREATE_ERROR_BUCKET(ctx->ctx, f, pass_bb); + } + } + + if (!(ctx->ctx->flags & FLAG_PRINTING)) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + "missing closing endif directive in parsed document" + " %s", r->filename); + } + + /* cleanup our temporary memory */ + apr_brigade_destroy(ctx->tmp_bb); + apr_pool_destroy(ctx->dpool); + + /* don't forget to finally insert the EOS bucket */ + APR_BRIGADE_INSERT_TAIL(pass_bb, b); + } + + /* if something's left over, pass it along */ + if (!APR_BRIGADE_EMPTY(pass_bb)) { + rv = ap_pass_brigade(f->next, pass_bb); + } + else { + rv = APR_SUCCESS; + } + + apr_brigade_destroy(pass_bb); + return rv; +} + +static void *create_includes_dir_config(apr_pool_t *p, char *dummy) +{ + include_dir_config *result = + (include_dir_config *)apr_palloc(p, sizeof(include_dir_config)); + enum xbithack *xbh = (enum xbithack *) apr_palloc(p, sizeof(enum xbithack)); + *xbh = DEFAULT_XBITHACK; + result->default_error_msg = DEFAULT_ERROR_MSG; + result->default_time_fmt = DEFAULT_TIME_FORMAT; + result->xbithack = xbh; + return result; +} + +static void *create_includes_server_config(apr_pool_t*p, server_rec *server) +{ + include_server_config *result = + (include_server_config *)apr_palloc(p, sizeof(include_server_config)); + result->default_end_tag = ENDING_SEQUENCE; + result->default_start_tag =STARTING_SEQUENCE; + result->start_tag_len = sizeof(STARTING_SEQUENCE)-1; + /* compile the pattern used by find_start_sequence */ + bndm_compile(&result->start_seq_pat, result->default_start_tag, + result->start_tag_len); + + result->undefinedEcho = apr_pstrdup(p,"(none)"); + result->undefinedEchoLen = strlen( result->undefinedEcho); + return result; +} +static const char *set_xbithack(cmd_parms *cmd, void *xbp, const char *arg) +{ + include_dir_config *conf = (include_dir_config *)xbp; + + if (!strcasecmp(arg, "off")) { + *conf->xbithack = xbithack_off; + } + else if (!strcasecmp(arg, "on")) { + *conf->xbithack = xbithack_on; + } + else if (!strcasecmp(arg, "full")) { + *conf->xbithack = xbithack_full; + } + else { + return "XBitHack must be set to Off, On, or Full"; + } + + return NULL; +} + +static int includes_setup(ap_filter_t *f) +{ + include_dir_config *conf = + (include_dir_config *)ap_get_module_config(f->r->per_dir_config, + &include_module); + + /* When our xbithack value isn't set to full or our platform isn't + * providing group-level protection bits or our group-level bits do not + * have group-execite on, we will set the no_local_copy value to 1 so + * that we will not send 304s. + */ + if ((*conf->xbithack != xbithack_full) + || !(f->r->finfo.valid & APR_FINFO_GPROT) + || !(f->r->finfo.protection & APR_GEXECUTE)) { + f->r->no_local_copy = 1; + } + + /* Don't allow ETag headers to be generated - see RFC2616 - 13.3.4. + * We don't know if we are going to be including a file or executing + * a program - in either case a strong ETag header will likely be invalid. + */ + apr_table_setn(f->r->notes, "no-etag", ""); + + return OK; +} + +static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b) +{ + request_rec *r = f->r; + ssi_ctx_t *ctx = f->ctx; + request_rec *parent; + include_dir_config *conf = + (include_dir_config *)ap_get_module_config(r->per_dir_config, + &include_module); + + include_server_config *sconf= ap_get_module_config(r->server->module_config, + &include_module); + + if (!(ap_allow_options(r) & OPT_INCLUDES)) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + "mod_include: Options +Includes (or IncludesNoExec) " + "wasn't set, INCLUDES filter removed"); + ap_remove_output_filter(f); + return ap_pass_brigade(f->next, b); + } + + if (!f->ctx) { + /* create context for this filter */ + f->ctx = ctx = apr_palloc(f->c->pool, sizeof(*ctx)); + ctx->ctx = apr_pcalloc(f->c->pool, sizeof(*ctx->ctx)); + ctx->ctx->pool = f->r->pool; + apr_pool_create(&ctx->dpool, ctx->ctx->pool); + + /* configuration data */ + ctx->end_seq_len = strlen(sconf->default_end_tag); + ctx->r = f->r; + + /* runtime data */ + ctx->tmp_bb = apr_brigade_create(ctx->ctx->pool, f->c->bucket_alloc); + ctx->seen_eos = 0; + ctx->state = PARSE_PRE_HEAD; + ctx->ctx->flags = (FLAG_PRINTING | FLAG_COND_TRUE); + if (ap_allow_options(f->r) & OPT_INCNOEXEC) { + ctx->ctx->flags |= FLAG_NO_EXEC; + } + ctx->ctx->if_nesting_level = 0; + ctx->ctx->re_string = NULL; + ctx->ctx->error_str_override = NULL; + ctx->ctx->time_str_override = NULL; + + ctx->ctx->error_str = conf->default_error_msg; + ctx->ctx->time_str = conf->default_time_fmt; + ctx->ctx->start_seq_pat = &sconf->start_seq_pat; + ctx->ctx->start_seq = sconf->default_start_tag; + ctx->ctx->start_seq_len = sconf->start_tag_len; + ctx->ctx->end_seq = sconf->default_end_tag; + + /* legacy compat stuff */ + ctx->ctx->state = PARSED; /* dummy */ + ctx->ctx->ssi_tag_brigade = apr_brigade_create(f->c->pool, + f->c->bucket_alloc); + ctx->ctx->status = APR_SUCCESS; + ctx->ctx->head_start_index = 0; + ctx->ctx->tag_start_index = 0; + ctx->ctx->tail_start_index = 0; + } + else { + ctx->ctx->bytes_parsed = 0; + } + + if ((parent = ap_get_module_config(r->request_config, &include_module))) { + /* Kludge --- for nested includes, we want to keep the subprocess + * environment of the base document (for compatibility); that means + * torquing our own last_modified date as well so that the + * LAST_MODIFIED variable gets reset to the proper value if the + * nested document resets <!--#config timefmt -->. + */ + r->subprocess_env = r->main->subprocess_env; + apr_pool_join(r->main->pool, r->pool); + r->finfo.mtime = r->main->finfo.mtime; + } + else { + /* we're not a nested include, so we create an initial + * environment */ + ap_add_common_vars(r); + ap_add_cgi_vars(r); + add_include_vars(r, conf->default_time_fmt); + } + /* Always unset the content-length. There is no way to know if + * the content will be modified at some point by send_parsed_content. + * It is very possible for us to not find any content in the first + * 9k of the file, but still have to modify the content of the file. + * If we are going to pass the file through send_parsed_content, then + * the content-length should just be unset. + */ + apr_table_unset(f->r->headers_out, "Content-Length"); + + /* Always unset the Last-Modified field - see RFC2616 - 13.3.4. + * We don't know if we are going to be including a file or executing + * a program which may change the Last-Modified header or make the + * content completely dynamic. Therefore, we can't support these + * headers. + * Exception: XBitHack full means we *should* set the Last-Modified field. + */ + + /* Assure the platform supports Group protections */ + if ((*conf->xbithack == xbithack_full) + && (r->finfo.valid & APR_FINFO_GPROT) + && (r->finfo.protection & APR_GEXECUTE)) { + ap_update_mtime(r, r->finfo.mtime); + ap_set_last_modified(r); + } + else { + apr_table_unset(f->r->headers_out, "Last-Modified"); + } + + /* add QUERY stuff to env cause it ain't yet */ + if (r->args) { + char *arg_copy = apr_pstrdup(r->pool, r->args); + + apr_table_setn(r->subprocess_env, "QUERY_STRING", r->args); + ap_unescape_url(arg_copy); + apr_table_setn(r->subprocess_env, "QUERY_STRING_UNESCAPED", + ap_escape_shell_cmd(r->pool, arg_copy)); + } + + return send_parsed_content(f, b); +} + +static void ap_register_include_handler(char *tag, include_handler_fn_t *func) +{ + apr_hash_set(include_hash, tag, strlen(tag), (const void *)func); +} + +static int include_post_config(apr_pool_t *p, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *s) +{ + include_hash = apr_hash_make(p); + + ssi_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_include_handler); + + if(ssi_pfn_register) { + ssi_pfn_register("if", handle_if); + ssi_pfn_register("set", handle_set); + ssi_pfn_register("else", handle_else); + ssi_pfn_register("elif", handle_elif); + ssi_pfn_register("echo", handle_echo); + ssi_pfn_register("endif", handle_endif); + ssi_pfn_register("fsize", handle_fsize); + ssi_pfn_register("config", handle_config); + ssi_pfn_register("include", handle_include); + ssi_pfn_register("flastmod", handle_flastmod); + ssi_pfn_register("printenv", handle_printenv); + } + return OK; +} + +static const char *set_default_error_msg(cmd_parms *cmd, void *mconfig, const char *msg) +{ + include_dir_config *conf = (include_dir_config *)mconfig; + conf->default_error_msg = apr_pstrdup(cmd->pool, msg); + return NULL; +} + +static const char *set_default_start_tag(cmd_parms *cmd, void *mconfig, const char *msg) +{ + include_server_config *conf; + conf= ap_get_module_config(cmd->server->module_config , &include_module); + conf->default_start_tag = apr_pstrdup(cmd->pool, msg); + conf->start_tag_len = strlen(conf->default_start_tag ); + bndm_compile(&conf->start_seq_pat, conf->default_start_tag, + conf->start_tag_len); + + return NULL; +} +static const char *set_undefined_echo(cmd_parms *cmd, void *mconfig, const char *msg) +{ + include_server_config *conf; + conf = ap_get_module_config(cmd->server->module_config, &include_module); + conf->undefinedEcho = apr_pstrdup(cmd->pool, msg); + conf->undefinedEchoLen = strlen(msg); + + return NULL; +} + + +static const char *set_default_end_tag(cmd_parms *cmd, void *mconfig, const char *msg) +{ + include_server_config *conf; + conf= ap_get_module_config(cmd->server->module_config , &include_module); + conf->default_end_tag = apr_pstrdup(cmd->pool, msg); + + return NULL; +} + +static const char *set_default_time_fmt(cmd_parms *cmd, void *mconfig, const char *fmt) +{ + include_dir_config *conf = (include_dir_config *)mconfig; + conf->default_time_fmt = apr_pstrdup(cmd->pool, fmt); + return NULL; +} + +/* + * Module definition and configuration data structs... + */ +static const command_rec includes_cmds[] = +{ + AP_INIT_TAKE1("XBitHack", set_xbithack, NULL, OR_OPTIONS, + "Off, On, or Full"), + AP_INIT_TAKE1("SSIErrorMsg", set_default_error_msg, NULL, OR_ALL, + "a string"), + AP_INIT_TAKE1("SSITimeFormat", set_default_time_fmt, NULL, OR_ALL, + "a strftime(3) formatted string"), + AP_INIT_TAKE1("SSIStartTag", set_default_start_tag, NULL, RSRC_CONF, + "SSI Start String Tag"), + AP_INIT_TAKE1("SSIEndTag", set_default_end_tag, NULL, RSRC_CONF, + "SSI End String Tag"), + AP_INIT_TAKE1("SSIUndefinedEcho", set_undefined_echo, NULL, RSRC_CONF, + "SSI Start String Tag"), + + {NULL} +}; + +static int include_fixup(request_rec *r) +{ + include_dir_config *conf; + + conf = (include_dir_config *) ap_get_module_config(r->per_dir_config, + &include_module); + + if (r->handler && (strcmp(r->handler, "server-parsed") == 0)) + { + if (!r->content_type || !*r->content_type) { + ap_set_content_type(r, "text/html"); + } + r->handler = "default-handler"; + } + else +#if defined(OS2) || defined(WIN32) || defined(NETWARE) + /* These OS's don't support xbithack. This is being worked on. */ + { + return DECLINED; + } +#else + { + if (*conf->xbithack == xbithack_off) { + return DECLINED; + } + + if (!(r->finfo.protection & APR_UEXECUTE)) { + return DECLINED; + } + + if (!r->content_type || strcmp(r->content_type, "text/html")) { + return DECLINED; + } + } +#endif + + /* We always return declined, because the default handler actually + * serves the file. All we have to do is add the filter. + */ + ap_add_output_filter("INCLUDES", NULL, r, r->connection); + return DECLINED; +} + +static void register_hooks(apr_pool_t *p) +{ + APR_REGISTER_OPTIONAL_FN(ap_ssi_get_tag_and_value); + APR_REGISTER_OPTIONAL_FN(ap_ssi_parse_string); + APR_REGISTER_OPTIONAL_FN(ap_register_include_handler); + ap_hook_post_config(include_post_config, NULL, NULL, APR_HOOK_REALLY_FIRST); + ap_hook_fixups(include_fixup, NULL, NULL, APR_HOOK_LAST); + ap_register_output_filter("INCLUDES", includes_filter, includes_setup, + AP_FTYPE_RESOURCE); +} + +module AP_MODULE_DECLARE_DATA include_module = +{ + STANDARD20_MODULE_STUFF, + create_includes_dir_config, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + create_includes_server_config,/* server config */ + NULL, /* merge server config */ + includes_cmds, /* command apr_table_t */ + register_hooks /* register hooks */ +}; diff --git a/rubbos/app/httpd-2.0.64/modules/filters/mod_include.dsp b/rubbos/app/httpd-2.0.64/modules/filters/mod_include.dsp new file mode 100644 index 00000000..52136219 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/mod_include.dsp @@ -0,0 +1,132 @@ +# Microsoft Developer Studio Project File - Name="mod_include" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_include - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_include.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_include.mak" CFG="mod_include - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_include - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_include - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_include - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /Zi /O2 /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_include_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:"Release/mod_include.so" /base:@..\..\os\win32\BaseAddr.ref,mod_include.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Release/mod_include.so" /base:@..\..\os\win32\BaseAddr.ref,mod_include.so /opt:ref + +!ELSEIF "$(CFG)" == "mod_include - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_include_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_include.so" /base:@..\..\os\win32\BaseAddr.ref,mod_include.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_include.so" /base:@..\..\os\win32\BaseAddr.ref,mod_include.so + +!ENDIF + +# Begin Target + +# Name "mod_include - Win32 Release" +# Name "mod_include - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_include.c +# End Source File +# Begin Source File + +SOURCE=.\mod_include.h +# End Source File +# Begin Source File + +SOURCE=.\mod_include.rc +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\win32ver.awk + +!IF "$(CFG)" == "mod_include - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_include.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_include.so "include_module for Apache" ../../include/ap_release.h > .\mod_include.rc + +# End Custom Build + +!ELSEIF "$(CFG)" == "mod_include - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_include.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_include.so "include_module for Apache" ../../include/ap_release.h > .\mod_include.rc + +# End Custom Build + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/rubbos/app/httpd-2.0.64/modules/filters/mod_include.exp b/rubbos/app/httpd-2.0.64/modules/filters/mod_include.exp new file mode 100644 index 00000000..112e1c4d --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/mod_include.exp @@ -0,0 +1 @@ +include_module diff --git a/rubbos/app/httpd-2.0.64/modules/filters/mod_include.h b/rubbos/app/httpd-2.0.64/modules/filters/mod_include.h new file mode 100644 index 00000000..6264b888 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/mod_include.h @@ -0,0 +1,206 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _MOD_INCLUDE_H +#define _MOD_INCLUDE_H 1 + +#include "apr_pools.h" +#include "apr_optional.h" + +#define STARTING_SEQUENCE "<!--#" +#define ENDING_SEQUENCE "-->" + +#define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]" +#define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z" +#define SIZEFMT_BYTES 0 +#define SIZEFMT_KMG 1 +#define TMP_BUF_SIZE 1024 +#if APR_CHARSET_EBCDIC +#define RAW_ASCII_CHAR(ch) apr_xlate_conv_byte(ap_hdrs_from_ascii, (unsigned char)ch) +#else /*APR_CHARSET_EBCDIC*/ +#define RAW_ASCII_CHAR(ch) (ch) +#endif /*APR_CHARSET_EBCDIC*/ + +/**************************************************************************** + * Used to keep context information during parsing of a request for SSI tags. + * This is especially useful if the tag stretches across multiple buckets or + * brigades. This keeps track of which buckets need to be replaced with the + * content generated by the SSI tag. + * + * state: PRE_HEAD - State prior to finding the first character of the + * STARTING_SEQUENCE. Next state is PARSE_HEAD. + * PARSE_HEAD - State entered once the first character of the + * STARTING_SEQUENCE is found and exited when the + * the full STARTING_SEQUENCE has been matched or + * a match failure occurs. Next state is PRE_HEAD + * or PARSE_TAG. + * PARSE_TAG - State entered once the STARTING sequence has been + * matched. It is exited when the first character in + * ENDING_SEQUENCE is found. Next state is PARSE_TAIL. + * PARSE_TAIL - State entered from PARSE_TAG state when the first + * character in ENDING_SEQUENCE is encountered. This + * state is exited when the ENDING_SEQUENCE has been + * completely matched, or when a match failure occurs. + * Next state is PARSE_TAG or PARSED. + * PARSED - State entered from PARSE_TAIL once the complete + * ENDING_SEQUENCE has been matched. The SSI tag is + * processed and the SSI buckets are replaced with the + * SSI content during this state. + * parse_pos: Current matched position within the STARTING_SEQUENCE or + * ENDING_SEQUENCE during the PARSE_HEAD and PARSE_TAIL states. + * This is especially useful when the sequence spans brigades. + * X_start_bucket: These point to the buckets containing the first character + * of the STARTING_SEQUENCE, the first non-whitespace + * character of the tag, and the first character in the + * ENDING_SEQUENCE (head_, tag_, and tail_ respectively). + * The buckets are kept intact until the PARSED state is + * reached, at which time the tag is consolidated and the + * buckets are released. The buckets that these point to + * have all been set aside in the ssi_tag_brigade (along + * with all of the intervening buckets). + * X_start_index: The index points within the specified bucket contents + * where the first character of the STARTING_SEQUENCE, + * the first non-whitespace character of the tag, and the + * first character in the ENDING_SEQUENCE can be found + * (head_, tag_, and tail_ respectively). + * combined_tag: Once the PARSED state is reached the tag is collected from + * the bucket(s) in the ssi_tag_brigade into this contiguous + * buffer. The buckets in the ssi_tag_brigade are released + * and the tag is processed. + * curr_tag_pos: Ptr to the combined_tag buffer indicating the current + * parse position. + * tag_length: The number of bytes in the actual tag (excluding the + * STARTING_SEQUENCE, leading and trailing whitespace, + * and ENDING_SEQUENCE). This length is computed as the + * buckets are parsed and set aside during the PARSE_TAG state. + * ssi_tag_brigade: The temporary brigade used by this filter to set aside + * the buckets containing parts of the ssi tag and headers. + */ + +/* I keep this stuff here, because of binary compat. It probably doesn't care, + * but who knows ...? + */ +#ifdef MOD_INCLUDE_REDESIGN +typedef enum {PRE_HEAD, BLOW_PARSE_HEAD, BLOW_PARSE_DIRECTIVE, PARSE_TAG, + BLOW_PARSE_TAIL, PARSED} states; +#else +typedef enum {PRE_HEAD, PARSE_HEAD, PARSE_DIRECTIVE, PARSE_TAG, PARSE_TAIL, + PARSED} states; +#endif + +/** forward referenced as it needs to be held on the context */ +typedef struct bndm_t bndm_t; + +typedef struct include_filter_ctx { + states state; + long flags; /* See the FLAG_XXXXX definitions. */ + int if_nesting_level; + apr_size_t parse_pos; + int bytes_parsed; + apr_status_t status; + int output_now; + int output_flush; + + apr_bucket *head_start_bucket; + apr_size_t head_start_index; + + apr_bucket *tag_start_bucket; + apr_size_t tag_start_index; + + apr_bucket *tail_start_bucket; + apr_size_t tail_start_index; + + char *combined_tag; + char *curr_tag_pos; + apr_size_t directive_length; + apr_size_t tag_length; + + char *error_str; + char *error_str_override; + char *time_str; + char *time_str_override; + apr_pool_t *pool; + + apr_bucket_brigade *ssi_tag_brigade; + bndm_t *start_seq_pat; + char *start_seq; + int start_seq_len; + char *end_seq; + char *re_string; + regmatch_t (*re_result)[10]; +} include_ctx_t; + +/* These flags are used to set flag bits. */ +#define FLAG_PRINTING 0x00000001 /* Printing conditional lines. */ +#define FLAG_COND_TRUE 0x00000002 /* Conditional eval'd to true. */ +#define FLAG_SIZE_IN_BYTES 0x00000004 /* Sizes displayed in bytes. */ +#define FLAG_NO_EXEC 0x00000008 /* No Exec in current context. */ + +/* These flags are used to clear flag bits. */ +#define FLAG_SIZE_ABBREV 0xFFFFFFFB /* Reset SIZE_IN_BYTES bit. */ +#define FLAG_CLEAR_PRINT_COND 0xFFFFFFFC /* Reset PRINTING and COND_TRUE*/ +#define FLAG_CLEAR_PRINTING 0xFFFFFFFE /* Reset just PRINTING bit. */ + +#define CREATE_ERROR_BUCKET(cntx, t_buck, h_ptr, ins_head) \ +{ \ + /* XXX: it'd probably be nice to use a pool bucket here */ \ + t_buck = apr_bucket_heap_create(cntx->error_str, \ + strlen(cntx->error_str), \ + NULL, h_ptr->list); \ + APR_BUCKET_INSERT_BEFORE(h_ptr, t_buck); \ + \ + if (ins_head == NULL) { \ + ins_head = t_buck; \ + } \ +} + +/* Make sure to check the return code rc. If it is anything other + * than APR_SUCCESS, then you should return this value up the + * call chain. + */ +#define SPLIT_AND_PASS_PRETAG_BUCKETS(brgd, cntxt, next, rc) \ +if ((APR_BRIGADE_EMPTY((cntxt)->ssi_tag_brigade)) && \ + ((cntxt)->head_start_bucket != NULL)) { \ + apr_bucket_brigade *tag_plus; \ + \ + tag_plus = apr_brigade_split((brgd), (cntxt)->head_start_bucket); \ + if ((cntxt)->output_flush) { \ + APR_BRIGADE_INSERT_TAIL((brgd), apr_bucket_flush_create((brgd)->bucket_alloc)); \ + } \ + (rc) = ap_pass_brigade((next), (brgd)); \ + (cntxt)->bytes_parsed = 0; \ + (brgd) = tag_plus; \ +} + + +typedef int (include_handler_fn_t)(include_ctx_t *ctx, apr_bucket_brigade **bb, + request_rec *r, ap_filter_t *f, apr_bucket *head_ptr, + apr_bucket **inserted_head); + +APR_DECLARE_OPTIONAL_FN(void, ap_ssi_get_tag_and_value, (include_ctx_t *ctx, + char **tag, + char **tag_val, + int dodecode)); +APR_DECLARE_OPTIONAL_FN(char*, ap_ssi_parse_string, (request_rec *r, + include_ctx_t *ctx, + const char *in, + char *out, + apr_size_t length, + int leave_name)); +APR_DECLARE_OPTIONAL_FN(void, ap_register_include_handler, + (char *tag, include_handler_fn_t *func)); + +#endif /* MOD_INCLUDE */ diff --git a/rubbos/app/httpd-2.0.64/modules/filters/mod_include.la b/rubbos/app/httpd-2.0.64/modules/filters/mod_include.la new file mode 100644 index 00000000..602915a6 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/mod_include.la @@ -0,0 +1,35 @@ +# mod_include.la - a libtool library file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='' + +# Names of this library. +library_names='' + +# The name of the static archive. +old_library='mod_include.a' + +# Libraries that this one depends upon. +dependency_libs=' -L/bottlenecks/rubbos/app/httpd-2.0.64/srclib/apr-util/xml/expat/lib' + +# Version information for mod_include. +current= +age= +revision= + +# Is this an already installed library? +installed=no + +# Should we warn about portability when linking against -modules? +shouldnotlink=yes + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='' diff --git a/rubbos/app/httpd-2.0.64/modules/filters/mod_include.lo b/rubbos/app/httpd-2.0.64/modules/filters/mod_include.lo new file mode 100644 index 00000000..9fdb766d --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/mod_include.lo @@ -0,0 +1,12 @@ +# mod_include.lo - a libtool object file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object='.libs/mod_include.o' + +# Name of the non-PIC object. +non_pic_object='mod_include.o' + diff --git a/rubbos/app/httpd-2.0.64/modules/filters/mod_include.o b/rubbos/app/httpd-2.0.64/modules/filters/mod_include.o Binary files differnew file mode 100644 index 00000000..d08f85f9 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/mod_include.o diff --git a/rubbos/app/httpd-2.0.64/modules/filters/modules.mk b/rubbos/app/httpd-2.0.64/modules/filters/modules.mk new file mode 100644 index 00000000..cd07d4a0 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/filters/modules.mk @@ -0,0 +1,5 @@ +mod_include.la: mod_include.lo + $(MOD_LINK) mod_include.lo $(MOD_INCLUDE_LDADD) +DISTCLEAN_TARGETS = modules.mk +static = mod_include.la +shared = |