summaryrefslogtreecommitdiffstats
path: root/modules/filters
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 06:33:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 06:33:50 +0000
commitfe39ffb8b90ae4e002ed73fe98617cd590abb467 (patch)
treeb80e5956907d8aeaaffe4e4f0c068c0e6157ce8b /modules/filters
parentInitial commit. (diff)
downloadapache2-fe39ffb8b90ae4e002ed73fe98617cd590abb467.tar.xz
apache2-fe39ffb8b90ae4e002ed73fe98617cd590abb467.zip
Adding upstream version 2.4.56.upstream/2.4.56
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'modules/filters')
-rw-r--r--modules/filters/.indent.pro54
-rw-r--r--modules/filters/Makefile.in3
-rw-r--r--modules/filters/NWGNUcharsetl257
-rw-r--r--modules/filters/NWGNUdeflate279
-rw-r--r--modules/filters/NWGNUextfiltr248
-rw-r--r--modules/filters/NWGNUmakefile273
-rw-r--r--modules/filters/NWGNUmod_data248
-rw-r--r--modules/filters/NWGNUmod_filter248
-rw-r--r--modules/filters/NWGNUmod_request248
-rw-r--r--modules/filters/NWGNUmodbuffer256
-rw-r--r--modules/filters/NWGNUmodsed259
-rw-r--r--modules/filters/NWGNUproxyhtml261
-rw-r--r--modules/filters/NWGNUratelimit256
-rw-r--r--modules/filters/NWGNUreflector256
-rw-r--r--modules/filters/NWGNUreqtimeout256
-rw-r--r--modules/filters/NWGNUsubstitute256
-rw-r--r--modules/filters/NWGNUxml2enc258
-rw-r--r--modules/filters/config.m4197
-rw-r--r--modules/filters/libsed.h172
-rw-r--r--modules/filters/mod_brotli.c608
-rw-r--r--modules/filters/mod_brotli.dep45
-rw-r--r--modules/filters/mod_brotli.dsp111
-rw-r--r--modules/filters/mod_brotli.mak353
-rw-r--r--modules/filters/mod_buffer.c353
-rw-r--r--modules/filters/mod_buffer.dep48
-rw-r--r--modules/filters/mod_buffer.dsp111
-rw-r--r--modules/filters/mod_buffer.mak353
-rw-r--r--modules/filters/mod_charset_lite.c1142
-rw-r--r--modules/filters/mod_charset_lite.dep60
-rw-r--r--modules/filters/mod_charset_lite.dsp111
-rw-r--r--modules/filters/mod_charset_lite.exp1
-rw-r--r--modules/filters/mod_charset_lite.mak353
-rw-r--r--modules/filters/mod_data.c255
-rw-r--r--modules/filters/mod_data.dep55
-rw-r--r--modules/filters/mod_data.dsp111
-rw-r--r--modules/filters/mod_data.mak353
-rw-r--r--modules/filters/mod_deflate.c1936
-rw-r--r--modules/filters/mod_deflate.dep52
-rw-r--r--modules/filters/mod_deflate.dsp111
-rw-r--r--modules/filters/mod_deflate.exp1
-rw-r--r--modules/filters/mod_deflate.mak353
-rw-r--r--modules/filters/mod_ext_filter.c954
-rw-r--r--modules/filters/mod_ext_filter.dep58
-rw-r--r--modules/filters/mod_ext_filter.dsp111
-rw-r--r--modules/filters/mod_ext_filter.exp1
-rw-r--r--modules/filters/mod_ext_filter.mak353
-rw-r--r--modules/filters/mod_filter.c767
-rw-r--r--modules/filters/mod_filter.dep50
-rw-r--r--modules/filters/mod_filter.dsp111
-rw-r--r--modules/filters/mod_filter.mak353
-rw-r--r--modules/filters/mod_include.c4238
-rw-r--r--modules/filters/mod_include.dep63
-rw-r--r--modules/filters/mod_include.dsp115
-rw-r--r--modules/filters/mod_include.exp1
-rw-r--r--modules/filters/mod_include.h120
-rw-r--r--modules/filters/mod_include.mak353
-rw-r--r--modules/filters/mod_proxy_html.c1353
-rw-r--r--modules/filters/mod_proxy_html.dep58
-rw-r--r--modules/filters/mod_proxy_html.dsp123
-rw-r--r--modules/filters/mod_proxy_html.mak352
-rw-r--r--modules/filters/mod_ratelimit.c340
-rw-r--r--modules/filters/mod_ratelimit.dep45
-rw-r--r--modules/filters/mod_ratelimit.dsp115
-rw-r--r--modules/filters/mod_ratelimit.h51
-rw-r--r--modules/filters/mod_ratelimit.mak353
-rw-r--r--modules/filters/mod_reflector.c231
-rw-r--r--modules/filters/mod_reflector.dep57
-rw-r--r--modules/filters/mod_reflector.dsp111
-rw-r--r--modules/filters/mod_reflector.mak353
-rw-r--r--modules/filters/mod_reqtimeout.c670
-rw-r--r--modules/filters/mod_reqtimeout.dep58
-rw-r--r--modules/filters/mod_reqtimeout.dsp111
-rw-r--r--modules/filters/mod_reqtimeout.mak353
-rw-r--r--modules/filters/mod_request.c393
-rw-r--r--modules/filters/mod_request.dep55
-rw-r--r--modules/filters/mod_request.dsp115
-rw-r--r--modules/filters/mod_request.mak353
-rw-r--r--modules/filters/mod_sed.c537
-rw-r--r--modules/filters/mod_sed.dep109
-rw-r--r--modules/filters/mod_sed.dsp135
-rw-r--r--modules/filters/mod_sed.mak380
-rw-r--r--modules/filters/mod_substitute.c766
-rw-r--r--modules/filters/mod_substitute.dep53
-rw-r--r--modules/filters/mod_substitute.dsp111
-rw-r--r--modules/filters/mod_substitute.mak353
-rw-r--r--modules/filters/mod_xml2enc.c676
-rw-r--r--modules/filters/mod_xml2enc.dep54
-rw-r--r--modules/filters/mod_xml2enc.dsp123
-rw-r--r--modules/filters/mod_xml2enc.h55
-rw-r--r--modules/filters/mod_xml2enc.mak352
-rw-r--r--modules/filters/regexp.c599
-rw-r--r--modules/filters/regexp.h112
-rw-r--r--modules/filters/sed.h61
-rw-r--r--modules/filters/sed0.c1026
-rw-r--r--modules/filters/sed1.c1110
95 files changed, 31071 insertions, 0 deletions
diff --git a/modules/filters/.indent.pro b/modules/filters/.indent.pro
new file mode 100644
index 0000000..a9fbe9f
--- /dev/null
+++ b/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/modules/filters/Makefile.in b/modules/filters/Makefile.in
new file mode 100644
index 0000000..167b343
--- /dev/null
+++ b/modules/filters/Makefile.in
@@ -0,0 +1,3 @@
+
+include $(top_srcdir)/build/special.mk
+
diff --git a/modules/filters/NWGNUcharsetl b/modules/filters/NWGNUcharsetl
new file mode 100644
index 0000000..9932635
--- /dev/null
+++ b/modules/filters/NWGNUcharsetl
@@ -0,0 +1,257 @@
+#
+# 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 += \
+ -DAP_WANT_DIR_TRANSLATION \
+ $(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 = charsetl
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Apache $(VERSION_STR) Charset Lite Module
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = charsetl
+
+#
+# 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 =
+
+#
+# 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)/charsetl.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_charset_lite.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 = \
+ $(PRELUDE) \
+ $(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 = \
+ @aprlib.imp \
+ @httpd.imp \
+ @libc.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ charset_lite_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 $(APBUILD)/NWGNUtail.inc
+
+
diff --git a/modules/filters/NWGNUdeflate b/modules/filters/NWGNUdeflate
new file mode 100644
index 0000000..7f7204d
--- /dev/null
+++ b/modules/filters/NWGNUdeflate
@@ -0,0 +1,279 @@
+#
+# 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 \
+ $(STDMOD)/ssl \
+ $(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 =
+
+#
+# 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)/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 = \
+ $(PRELUDE) \
+ $(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 = \
+ @aprlib.imp \
+ @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
+ $(call COPY,$(OBJDIR)/*.nlm, $(INSTALLBASE)/modules/)
+
+#
+# Any specialized rules here
+#
+
+vpath %.c $(ZLIBSDK)
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APBUILD)/NWGNUtail.inc
+
+
diff --git a/modules/filters/NWGNUextfiltr b/modules/filters/NWGNUextfiltr
new file mode 100644
index 0000000..1fd57cb
--- /dev/null
+++ b/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 =
+
+#
+# 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 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 = \
+ $(PRELUDE) \
+ $(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 = \
+ @aprlib.imp \
+ @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 $(APBUILD)/NWGNUtail.inc
+
+
diff --git a/modules/filters/NWGNUmakefile b/modules/filters/NWGNUmakefile
new file mode 100644
index 0000000..922abac
--- /dev/null
+++ b/modules/filters/NWGNUmakefile
@@ -0,0 +1,273 @@
+#
+# 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 \
+ $(OBJDIR)/charsetl.nlm \
+ $(OBJDIR)/mod_data.nlm \
+ $(OBJDIR)/mod_filter.nlm \
+ $(OBJDIR)/mod_request.nlm \
+ $(OBJDIR)/substitute.nlm \
+ $(OBJDIR)/modsed.nlm \
+ $(OBJDIR)/modbuffer.nlm \
+ $(OBJDIR)/ratelimit.nlm \
+ $(OBJDIR)/reqtimeout.nlm \
+ $(OBJDIR)/reflector.nlm \
+ $(EOLIST)
+
+# If the zlib library source exists then build the mod_deflate module
+ifneq "$(ZLIBSDK)" ""
+ifeq "$(wildcard $(ZLIBSDK))" "$(ZLIBSDK)"
+TARGET_nlm += $(OBJDIR)/deflate.nlm \
+ $(EOLIST)
+endif
+endif
+
+# If the libxml2 library source exists then build the mod_proxy_html module
+ifneq "$(LIBXML2SDK)" ""
+ifeq "$(wildcard $(LIBXML2SDK))" "$(LIBXML2SDK)"
+TARGET_nlm += \
+ $(OBJDIR)/proxyhtml.nlm \
+ $(OBJDIR)/xml2enc.nlm \
+ $(EOLIST)
+endif
+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
+ $(call COPY,$(OBJDIR)/*.nlm, $(INSTALLBASE)/modules/)
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APBUILD)/NWGNUtail.inc
+
+
diff --git a/modules/filters/NWGNUmod_data b/modules/filters/NWGNUmod_data
new file mode 100644
index 0000000..7f46e9a
--- /dev/null
+++ b/modules/filters/NWGNUmod_data
@@ -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 = mod_data
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Apache $(VERSION_STR) Data Module
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = Filter 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 =
+
+#
+# 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)/$(NLM_NAME).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)/$(NLM_NAME).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 = \
+ $(PRELUDE) \
+ $(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 = \
+ @aprlib.imp \
+ @httpd.imp \
+ @libc.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ data_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 $(APBUILD)/NWGNUtail.inc
+
+
diff --git a/modules/filters/NWGNUmod_filter b/modules/filters/NWGNUmod_filter
new file mode 100644
index 0000000..bf323ee
--- /dev/null
+++ b/modules/filters/NWGNUmod_filter
@@ -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 = mod_filter
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Apache $(VERSION_STR) Filter Module
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = Filter 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 =
+
+#
+# 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)/mod_filter.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_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 = \
+ $(PRELUDE) \
+ $(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 = \
+ @aprlib.imp \
+ @httpd.imp \
+ @libc.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ 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 $(APBUILD)/NWGNUtail.inc
+
+
diff --git a/modules/filters/NWGNUmod_request b/modules/filters/NWGNUmod_request
new file mode 100644
index 0000000..21f53cc
--- /dev/null
+++ b/modules/filters/NWGNUmod_request
@@ -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 = mod_request
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Apache $(VERSION_STR) Request Module
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = Request 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 =
+
+#
+# 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)/mod_request.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_request.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 = \
+ $(PRELUDE) \
+ $(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 = \
+ @aprlib.imp \
+ @httpd.imp \
+ @libc.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ request_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 $(APBUILD)/NWGNUtail.inc
+
+
diff --git a/modules/filters/NWGNUmodbuffer b/modules/filters/NWGNUmodbuffer
new file mode 100644
index 0000000..7652a35
--- /dev/null
+++ b/modules/filters/NWGNUmodbuffer
@@ -0,0 +1,256 @@
+#
+# 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 = modbuffer
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Apache $(VERSION_STR) Buffer Module
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = modbuffer
+
+#
+# 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 =
+
+#
+# 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)/modbuffer.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_buffer.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 = \
+ $(PRELUDE) \
+ $(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 = \
+ @aprlib.imp \
+ @httpd.imp \
+ @libc.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ buffer_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 $(APBUILD)/NWGNUtail.inc
+
+
diff --git a/modules/filters/NWGNUmodsed b/modules/filters/NWGNUmodsed
new file mode 100644
index 0000000..158acb9
--- /dev/null
+++ b/modules/filters/NWGNUmodsed
@@ -0,0 +1,259 @@
+#
+# 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 = modsed
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Apache $(VERSION_STR) SED Module
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = modsed 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 =
+
+#
+# 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)/modsed.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_sed.o \
+ $(OBJDIR)/regexp.o \
+ $(OBJDIR)/sed0.o \
+ $(OBJDIR)/sed1.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 = \
+ $(PRELUDE) \
+ $(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 = \
+ @aprlib.imp \
+ @httpd.imp \
+ @libc.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ sed_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 $(APBUILD)/NWGNUtail.inc
+
+
diff --git a/modules/filters/NWGNUproxyhtml b/modules/filters/NWGNUproxyhtml
new file mode 100644
index 0000000..5337f3b
--- /dev/null
+++ b/modules/filters/NWGNUproxyhtml
@@ -0,0 +1,261 @@
+#
+# 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 += \
+ $(LIBXML2SDK)/include \
+ $(APR)/include \
+ $(APRUTIL)/include \
+ $(AP_WORK)/include \
+ $(AP_WORK)/modules/http \
+ $(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 += \
+ -L$(LIBXML2SDK)/lib -llibxml2.lib \
+ $(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 = proxyhtml
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy HTML Module
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = Proxy HTTP 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 =
+
+#
+# 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)/$(NLM_NAME).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_proxy_html.o \
+ $(OBJDIR)/libprews.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 = \
+ $(PRELUDE) \
+ $(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 \
+ proxy \
+ $(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 = \
+ @aprlib.imp \
+ @httpd.imp \
+ @libc.imp \
+ $(EOLIST)
+
+# Don't link with Winsock if standard sockets are being used
+ifndef USE_STDSOCKETS
+FILES_nlm_Ximports += @ws2nlm.imp \
+ $(EOLIST)
+endif
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ proxy_html_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
+#
+
+vpath %.c ../arch/netware
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APBUILD)/NWGNUtail.inc
+
+
diff --git a/modules/filters/NWGNUratelimit b/modules/filters/NWGNUratelimit
new file mode 100644
index 0000000..f75c814
--- /dev/null
+++ b/modules/filters/NWGNUratelimit
@@ -0,0 +1,256 @@
+#
+# 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 = ratelimit
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Apache $(VERSION_STR) Rate Limit Module
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = ratelimit
+
+#
+# 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 =
+
+#
+# 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)/ratelimit.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_ratelimit.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 = \
+ $(PRELUDE) \
+ $(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 = \
+ @aprlib.imp \
+ @httpd.imp \
+ @libc.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ ratelimit_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 $(APBUILD)/NWGNUtail.inc
+
+
diff --git a/modules/filters/NWGNUreflector b/modules/filters/NWGNUreflector
new file mode 100644
index 0000000..67ca26d
--- /dev/null
+++ b/modules/filters/NWGNUreflector
@@ -0,0 +1,256 @@
+#
+# 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 = reflector
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Apache $(VERSION_STR) Reflector Module
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = reflector
+
+#
+# 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 =
+
+#
+# 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)/reflector.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_reflector.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 = \
+ $(PRELUDE) \
+ $(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 = \
+ @aprlib.imp \
+ @httpd.imp \
+ @libc.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ reflector_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 $(APBUILD)/NWGNUtail.inc
+
+
diff --git a/modules/filters/NWGNUreqtimeout b/modules/filters/NWGNUreqtimeout
new file mode 100644
index 0000000..a99f82c
--- /dev/null
+++ b/modules/filters/NWGNUreqtimeout
@@ -0,0 +1,256 @@
+#
+# 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 = reqtimeout
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Apache $(VERSION_STR) Request Timeout Module
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = $(NLM_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 = 8192
+
+
+#
+# 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)/$(NLM_NAME).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_reqtimeout.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 = \
+ $(PRELUDE) \
+ $(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 = \
+ @aprlib.imp \
+ @httpd.imp \
+ @libc.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ reqtimeout_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 $(APBUILD)/NWGNUtail.inc
+
+
diff --git a/modules/filters/NWGNUsubstitute b/modules/filters/NWGNUsubstitute
new file mode 100644
index 0000000..e5775f2
--- /dev/null
+++ b/modules/filters/NWGNUsubstitute
@@ -0,0 +1,256 @@
+#
+# 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 = substitute
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Apache $(VERSION_STR) Substitute Module
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = Substitute 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 =
+
+#
+# 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)/substitute.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_substitute.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 = \
+ $(PRELUDE) \
+ $(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 = \
+ @aprlib.imp \
+ @httpd.imp \
+ @libc.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ substitute_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 $(APBUILD)/NWGNUtail.inc
+
+
diff --git a/modules/filters/NWGNUxml2enc b/modules/filters/NWGNUxml2enc
new file mode 100644
index 0000000..117832e
--- /dev/null
+++ b/modules/filters/NWGNUxml2enc
@@ -0,0 +1,258 @@
+#
+# 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 += \
+ $(LIBXML2SDK)/include \
+ $(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 += \
+ -L$(LIBXML2SDK)/lib -llibxml2.lib \
+ $(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 = xml2enc
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Apache $(VERSION_STR) xml2enc Module
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = Substitute 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 =
+
+#
+# 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)/$(NLM_NAME).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_xml2enc.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 = \
+ $(PRELUDE) \
+ $(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 = \
+ @aprlib.imp \
+ @httpd.imp \
+ @libc.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ xml2enc_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 $(APBUILD)/NWGNUtail.inc
+
+
diff --git a/modules/filters/config.m4 b/modules/filters/config.m4
new file mode 100644
index 0000000..810f0d7
--- /dev/null
+++ b/modules/filters/config.m4
@@ -0,0 +1,197 @@
+dnl modules enabled in this directory by default
+
+dnl APACHE_MODULE(name, helptext[, objects[, structname[, default[, config]]]])
+
+APACHE_MODPATH_INIT(filters)
+
+APACHE_MODULE(buffer, Filter Buffering, , , most)
+APACHE_MODULE(data, RFC2397 data encoder, , , )
+APACHE_MODULE(ratelimit, Output Bandwidth Limiting, , , most)
+APACHE_MODULE(reqtimeout, Limit time waiting for request from client, , , yes)
+APACHE_MODULE(ext_filter, external filter module, , , most)
+APACHE_MODULE(request, Request Body Filtering, , , most)
+APACHE_MODULE(include, Server Side Includes, , , most)
+APACHE_MODULE(filter, Smart Filtering, , , yes)
+APACHE_MODULE(reflector, Reflect request through the output filter stack, , , )
+APACHE_MODULE(substitute, response content rewrite-like filtering, , , most)
+
+sed_obj="mod_sed.lo sed0.lo sed1.lo regexp.lo"
+APACHE_MODULE(sed, filter request and/or response bodies through sed, $sed_obj, , most, [
+ if test "x$enable_sed" = "xshared"; then
+ # The only symbol which needs to be exported is the module
+ # structure, so ask libtool to hide libsed internals:
+ APR_ADDTO(MOD_SED_LDADD, [-export-symbols-regex sed_module])
+ fi
+])
+
+if test "$ac_cv_ebcdic" = "yes"; then
+# mod_charset_lite can be very useful on an ebcdic system,
+# so include it by default
+ APACHE_MODULE(charset_lite, character set translation. Enabled by default only on EBCDIC systems., , , yes)
+else
+ APACHE_MODULE(charset_lite, character set translation. Enabled by default only on EBCDIC systems., , , )
+fi
+
+
+APACHE_MODULE(deflate, Deflate transfer encoding support, , , most, [
+ AC_ARG_WITH(z, APACHE_HELP_STRING(--with-z=PATH,use a specific zlib library),
+ [
+ if test "x$withval" != "xyes" && test "x$withval" != "x"; then
+ ap_zlib_base="$withval"
+ ap_zlib_with="yes"
+ 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=$INCLUDES
+ ap_save_ldflags=$LDFLAGS
+ ap_save_cppflags=$CPPFLAGS
+ ap_zlib_ldflags=""
+ if test "$ap_zlib_base" != "/usr"; then
+ APR_ADDTO(INCLUDES, [-I${ap_zlib_base}/include])
+ APR_ADDTO(MOD_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])
+ APR_ADDTO(ap_zlib_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])
+ APR_ADDTO(ap_zlib_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)
+ APR_ADDTO(MOD_DEFLATE_LDADD, [$ap_zlib_ldflags -lz])],
+ [AC_MSG_RESULT(not found)
+ enable_deflate=no
+ if test "x$ap_zlib_with" = "x"; then
+ AC_MSG_WARN([... Error, zlib was missing or unusable])
+ else
+ AC_MSG_ERROR([... Error, zlib was missing or unusable])
+ fi
+ ])
+ INCLUDES=$ap_save_includes
+ LDFLAGS=$ap_save_ldflags
+ CPPFLAGS=$ap_save_cppflags
+ APR_REMOVEFROM(LIBS, [-lz])
+ fi
+])
+
+AC_DEFUN([FIND_LIBXML2], [
+ AC_CACHE_CHECK([for libxml2], [ac_cv_libxml2], [
+ AC_ARG_WITH(libxml2,
+ [APACHE_HELP_STRING(--with-libxml2=PATH,location for libxml2)],
+ [test_paths="${with_libxml2}/include/libxml2 ${with_libxml2}/include ${with_libxml2}"],
+ [test_paths="/usr/include/libxml2 /usr/local/include/libxml2 /usr/include /usr/local/include"]
+ )
+ AC_MSG_CHECKING(for libxml2)
+ xml2_path=""
+ for x in ${test_paths}; do
+ if test -f "${x}/libxml/parser.h"; then
+ xml2_path="${x}"
+ break
+ fi
+ done
+ if test -n "${xml2_path}" ; then
+ ac_cv_libxml2=yes
+ XML2_INCLUDES="${xml2_path}"
+ else
+ ac_cv_libxml2=no
+ fi
+ ])
+])
+
+APACHE_MODULE(xml2enc, i18n support for markup filters, , , , [
+ FIND_LIBXML2
+ if test "$ac_cv_libxml2" = "yes" ; then
+ APR_ADDTO(MOD_CFLAGS, [-I${XML2_INCLUDES}])
+ APR_ADDTO(MOD_XML2ENC_LDADD, [-lxml2])
+ else
+ enable_xml2enc=no
+ fi
+])
+APACHE_MODULE(proxy_html, Fix HTML Links in a Reverse Proxy, , , , [
+ FIND_LIBXML2
+ if test "$ac_cv_libxml2" = "yes" ; then
+ APR_ADDTO(MOD_CFLAGS, [-I${XML2_INCLUDES}])
+ APR_ADDTO(MOD_PROXY_HTML_LDADD, [-lxml2])
+ else
+ enable_proxy_html=no
+ fi
+]
+)
+
+APACHE_MODULE(brotli, Brotli compression support, , , most, [
+ AC_ARG_WITH(brotli, APACHE_HELP_STRING(--with-brotli=PATH,Brotli installation directory),[
+ if test "$withval" != "yes" -a "x$withval" != "x"; then
+ ap_brotli_base="$withval"
+ ap_brotli_with=yes
+ fi
+ ])
+ ap_brotli_found=no
+ if test -n "$ap_brotli_base"; then
+ ap_save_cppflags=$CPPFLAGS
+ APR_ADDTO(CPPFLAGS, [-I${ap_brotli_base}/include])
+ AC_MSG_CHECKING([for Brotli library >= 0.6.0 via prefix])
+ AC_TRY_COMPILE(
+ [#include <brotli/encode.h>],[
+const uint8_t *o = BrotliEncoderTakeOutput((BrotliEncoderState*)0, (size_t*)0);
+if (o) return *o;],
+ [AC_MSG_RESULT(yes)
+ ap_brotli_found=yes
+ ap_brotli_cflags="-I${ap_brotli_base}/include"
+ ap_brotli_libs="-L${ap_brotli_base}/lib -lbrotlienc -lbrotlicommon"],
+ [AC_MSG_RESULT(no)]
+ )
+ CPPFLAGS=$ap_save_cppflags
+ else
+ if test -n "$PKGCONFIG"; then
+ AC_MSG_CHECKING([for Brotli library >= 0.6.0 via pkg-config])
+ if $PKGCONFIG --exists "libbrotlienc >= 0.6.0"; then
+ AC_MSG_RESULT(yes)
+ ap_brotli_found=yes
+ ap_brotli_cflags=`$PKGCONFIG libbrotlienc --cflags`
+ ap_brotli_libs=`$PKGCONFIG libbrotlienc --libs`
+ else
+ AC_MSG_RESULT(no)
+ fi
+ fi
+ fi
+ if test "$ap_brotli_found" = "yes"; then
+ APR_ADDTO(MOD_CFLAGS, [$ap_brotli_cflags])
+ APR_ADDTO(MOD_BROTLI_LDADD, [$ap_brotli_libs])
+ if test "$enable_brotli" = "shared"; then
+ dnl The only symbol which needs to be exported is the module
+ dnl structure, so ask libtool to hide everything else:
+ APR_ADDTO(MOD_BROTLI_LDADD, [-export-symbols-regex brotli_module])
+ fi
+ else
+ enable_brotli=no
+ if test "$ap_brotli_with" = "yes"; then
+ AC_MSG_ERROR([Brotli library was missing or unusable])
+ fi
+ fi
+])
+
+APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/$modpath_current])
+
+APACHE_MODPATH_FINISH
diff --git a/modules/filters/libsed.h b/modules/filters/libsed.h
new file mode 100644
index 0000000..0256b1e
--- /dev/null
+++ b/modules/filters/libsed.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2005, 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1984 AT&T
+ * All Rights Reserved
+ *
+ * Licensed 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 LIBSED_H
+#define LIBSED_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <limits.h>
+
+#include "apr_file_io.h"
+
+#define SED_NLINES 256
+#define SED_DEPTH 20
+#define SED_LABSIZE 50
+#define SED_ABUFSIZE 20
+
+typedef struct sed_reptr_s sed_reptr_t;
+
+struct sed_reptr_s {
+ sed_reptr_t *next;
+ char *ad1;
+ char *ad2;
+ char *re1;
+ sed_reptr_t *lb1;
+ char *rhs;
+ int findex;
+ char command;
+ int gfl;
+ char pfl;
+ char negfl;
+ int nrep;
+};
+
+typedef struct sed_label_s sed_label_t;
+
+struct sed_label_s {
+ char asc[9];
+ sed_reptr_t *chain;
+ sed_reptr_t *address;
+};
+
+typedef apr_status_t (sed_err_fn_t)(void *data, const char *error);
+typedef apr_status_t (sed_write_fn_t)(void *ctx, char *buf, apr_size_t sz);
+
+typedef struct sed_commands_s sed_commands_t;
+#define NWFILES 11 /* 10 plus one for standard output */
+
+struct sed_commands_s {
+ sed_err_fn_t *errfn;
+ void *data;
+
+ apr_size_t lsize;
+ char *linebuf;
+ char *lbend;
+ const char *saveq;
+
+ char *cp;
+ char *lastre;
+ char *respace;
+ char sseof;
+ char *reend;
+ const char *earg;
+ int eflag;
+ int gflag;
+ int nflag;
+ apr_int64_t tlno[SED_NLINES];
+ int nlno;
+ int depth;
+
+ char *fname[NWFILES];
+ int nfiles;
+
+ sed_label_t ltab[SED_LABSIZE];
+ sed_label_t *labtab;
+ sed_label_t *lab;
+ sed_label_t *labend;
+
+ sed_reptr_t **cmpend[SED_DEPTH];
+ sed_reptr_t *ptrspace;
+ sed_reptr_t *ptrend;
+ sed_reptr_t *rep;
+ int nrep;
+ apr_pool_t *pool;
+ int canbefinal;
+};
+
+typedef struct sed_eval_s sed_eval_t;
+
+struct sed_eval_s {
+ sed_err_fn_t *errfn;
+ sed_write_fn_t *writefn;
+ void *data;
+
+ sed_commands_t *commands;
+
+ apr_int64_t lnum;
+ void *fout;
+
+ apr_size_t lsize;
+ char *linebuf;
+ char *lspend;
+
+ apr_size_t hsize;
+ char *holdbuf;
+ char *hspend;
+
+ apr_size_t gsize;
+ char *genbuf;
+ char *lcomend;
+
+ apr_file_t *fcode[NWFILES];
+ sed_reptr_t *abuf[SED_ABUFSIZE];
+ sed_reptr_t **aptr;
+ sed_reptr_t *pending;
+ unsigned char *inar;
+ int nrep;
+
+ int dolflag;
+ int sflag;
+ int jflag;
+ int delflag;
+ int lreadyflag;
+ int quitflag;
+ int finalflag;
+ int numpass;
+ int nullmatch;
+ int col;
+ apr_pool_t *pool;
+};
+
+apr_status_t sed_init_commands(sed_commands_t *commands, sed_err_fn_t *errfn, void *data,
+ apr_pool_t *p);
+apr_status_t sed_compile_string(sed_commands_t *commands, const char *s);
+apr_status_t sed_compile_file(sed_commands_t *commands, apr_file_t *fin);
+char* sed_get_finalize_error(const sed_commands_t *commands, apr_pool_t* pool);
+int sed_canbe_finalized(const sed_commands_t *commands);
+void sed_destroy_commands(sed_commands_t *commands);
+
+apr_status_t sed_init_eval(sed_eval_t *eval, sed_commands_t *commands,
+ sed_err_fn_t *errfn, void *data,
+ sed_write_fn_t *writefn, apr_pool_t *p);
+apr_status_t sed_reset_eval(sed_eval_t *eval, sed_commands_t *commands, sed_err_fn_t *errfn, void *data);
+apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, apr_size_t bufsz, void *fout);
+apr_status_t sed_eval_file(sed_eval_t *eval, apr_file_t *fin, void *fout);
+apr_status_t sed_finalize_eval(sed_eval_t *eval, void *f);
+void sed_destroy_eval(sed_eval_t *eval);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSED_H */
diff --git a/modules/filters/mod_brotli.c b/modules/filters/mod_brotli.c
new file mode 100644
index 0000000..0f7d770
--- /dev/null
+++ b/modules/filters/mod_brotli.c
@@ -0,0 +1,608 @@
+/* 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.
+ */
+
+#include "httpd.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "apr_strings.h"
+
+#include <brotli/encode.h>
+
+module AP_MODULE_DECLARE_DATA brotli_module;
+
+typedef enum {
+ ETAG_MODE_ADDSUFFIX = 0,
+ ETAG_MODE_NOCHANGE = 1,
+ ETAG_MODE_REMOVE = 2
+} etag_mode_e;
+
+typedef struct brotli_server_config_t {
+ int quality;
+ int lgwin;
+ int lgblock;
+ etag_mode_e etag_mode;
+ const char *note_ratio_name;
+ const char *note_input_name;
+ const char *note_output_name;
+} brotli_server_config_t;
+
+static void *create_server_config(apr_pool_t *p, server_rec *s)
+{
+ brotli_server_config_t *conf = apr_pcalloc(p, sizeof(*conf));
+
+ /* These default values allow mod_brotli to behave similarly to
+ * mod_deflate in terms of compression speed and memory usage.
+ *
+ * The idea is that since Brotli (generally) gives better compression
+ * ratio than Deflate, simply enabling mod_brotli on the server
+ * will reduce the amount of transferred data while keeping everything
+ * else unchanged. See https://quixdb.github.io/squash-benchmark/
+ */
+ conf->quality = 5;
+ conf->lgwin = 18;
+ /* Zero is a special value for BROTLI_PARAM_LGBLOCK that allows
+ * Brotli to automatically select the optimal input block size based
+ * on other encoder parameters. See enc/quality.h: ComputeLgBlock().
+ */
+ conf->lgblock = 0;
+ conf->etag_mode = ETAG_MODE_ADDSUFFIX;
+
+ return conf;
+}
+
+static const char *set_filter_note(cmd_parms *cmd, void *dummy,
+ const char *arg1, const char *arg2)
+{
+ brotli_server_config_t *conf =
+ ap_get_module_config(cmd->server->module_config, &brotli_module);
+
+ if (!arg2) {
+ conf->note_ratio_name = arg1;
+ return NULL;
+ }
+
+ if (ap_cstr_casecmp(arg1, "Ratio") == 0) {
+ conf->note_ratio_name = arg2;
+ }
+ else if (ap_cstr_casecmp(arg1, "Input") == 0) {
+ conf->note_input_name = arg2;
+ }
+ else if (ap_cstr_casecmp(arg1, "Output") == 0) {
+ conf->note_output_name = arg2;
+ }
+ else {
+ return apr_psprintf(cmd->pool, "Unknown BrotliFilterNote type '%s'",
+ arg1);
+ }
+
+ return NULL;
+}
+
+static const char *set_compression_quality(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ brotli_server_config_t *conf =
+ ap_get_module_config(cmd->server->module_config, &brotli_module);
+ int val = atoi(arg);
+
+ if (val < 0 || val > 11) {
+ return "BrotliCompressionQuality must be between 0 and 11";
+ }
+
+ conf->quality = val;
+ return NULL;
+}
+
+static const char *set_compression_lgwin(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ brotli_server_config_t *conf =
+ ap_get_module_config(cmd->server->module_config, &brotli_module);
+ int val = atoi(arg);
+
+ if (val < 10 || val > 24) {
+ return "BrotliCompressionWindow must be between 10 and 24";
+ }
+
+ conf->lgwin = val;
+ return NULL;
+}
+
+static const char *set_compression_lgblock(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ brotli_server_config_t *conf =
+ ap_get_module_config(cmd->server->module_config, &brotli_module);
+ int val = atoi(arg);
+
+ if (val < 16 || val > 24) {
+ return "BrotliCompressionMaxInputBlock must be between 16 and 24";
+ }
+
+ conf->lgblock = val;
+ return NULL;
+}
+
+static const char *set_etag_mode(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ brotli_server_config_t *conf =
+ ap_get_module_config(cmd->server->module_config, &brotli_module);
+
+ if (ap_cstr_casecmp(arg, "AddSuffix") == 0) {
+ conf->etag_mode = ETAG_MODE_ADDSUFFIX;
+ }
+ else if (ap_cstr_casecmp(arg, "NoChange") == 0) {
+ conf->etag_mode = ETAG_MODE_NOCHANGE;
+ }
+ else if (ap_cstr_casecmp(arg, "Remove") == 0) {
+ conf->etag_mode = ETAG_MODE_REMOVE;
+ }
+ else {
+ return "BrotliAlterETag accepts only 'AddSuffix', 'NoChange' and 'Remove'";
+ }
+
+ return NULL;
+}
+
+typedef struct brotli_ctx_t {
+ BrotliEncoderState *state;
+ apr_bucket_brigade *bb;
+ apr_off_t total_in;
+ apr_off_t total_out;
+} brotli_ctx_t;
+
+static void *alloc_func(void *opaque, size_t size)
+{
+ return apr_bucket_alloc(size, opaque);
+}
+
+static void free_func(void *opaque, void *block)
+{
+ if (block) {
+ apr_bucket_free(block);
+ }
+}
+
+static apr_status_t cleanup_ctx(void *data)
+{
+ brotli_ctx_t *ctx = data;
+
+ BrotliEncoderDestroyInstance(ctx->state);
+ ctx->state = NULL;
+ return APR_SUCCESS;
+}
+
+static brotli_ctx_t *create_ctx(int quality,
+ int lgwin,
+ int lgblock,
+ apr_bucket_alloc_t *alloc,
+ apr_pool_t *pool)
+{
+ brotli_ctx_t *ctx = apr_pcalloc(pool, sizeof(*ctx));
+
+ ctx->state = BrotliEncoderCreateInstance(alloc_func, free_func, alloc);
+ BrotliEncoderSetParameter(ctx->state, BROTLI_PARAM_QUALITY, quality);
+ BrotliEncoderSetParameter(ctx->state, BROTLI_PARAM_LGWIN, lgwin);
+ BrotliEncoderSetParameter(ctx->state, BROTLI_PARAM_LGBLOCK, lgblock);
+ apr_pool_cleanup_register(pool, ctx, cleanup_ctx, apr_pool_cleanup_null);
+
+ ctx->bb = apr_brigade_create(pool, alloc);
+ ctx->total_in = 0;
+ ctx->total_out = 0;
+
+ return ctx;
+}
+
+static apr_status_t process_chunk(brotli_ctx_t *ctx,
+ const void *data,
+ apr_size_t len,
+ ap_filter_t *f)
+{
+ const apr_byte_t *next_in = data;
+ apr_size_t avail_in = len;
+
+ while (avail_in > 0) {
+ apr_byte_t *next_out = NULL;
+ apr_size_t avail_out = 0;
+
+ if (!BrotliEncoderCompressStream(ctx->state,
+ BROTLI_OPERATION_PROCESS,
+ &avail_in, &next_in,
+ &avail_out, &next_out, NULL)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(03459)
+ "Error while compressing data");
+ return APR_EGENERAL;
+ }
+
+ if (BrotliEncoderHasMoreOutput(ctx->state)) {
+ apr_size_t output_len = 0;
+ const apr_byte_t *output;
+ apr_status_t rv;
+ apr_bucket *b;
+
+ /* Drain the accumulated output. Avoid copying the data by
+ * wrapping a pointer to the internal output buffer and passing
+ * it down to the next filter. The pointer is only valid until
+ * the next call to BrotliEncoderCompressStream(), but we're okay
+ * with that, since the brigade is cleaned up right after the
+ * ap_pass_brigade() call.
+ */
+ output = BrotliEncoderTakeOutput(ctx->state, &output_len);
+ ctx->total_out += output_len;
+
+ b = apr_bucket_transient_create((const char *)output, output_len,
+ ctx->bb->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
+
+ rv = ap_pass_brigade(f->next, ctx->bb);
+ apr_brigade_cleanup(ctx->bb);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ }
+ }
+
+ ctx->total_in += len;
+ return APR_SUCCESS;
+}
+
+static apr_status_t flush(brotli_ctx_t *ctx,
+ BrotliEncoderOperation op,
+ ap_filter_t *f)
+{
+ while (1) {
+ const apr_byte_t *next_in = NULL;
+ apr_size_t avail_in = 0;
+ apr_byte_t *next_out = NULL;
+ apr_size_t avail_out = 0;
+ apr_size_t output_len;
+ const apr_byte_t *output;
+ apr_bucket *b;
+
+ if (!BrotliEncoderCompressStream(ctx->state, op,
+ &avail_in, &next_in,
+ &avail_out, &next_out, NULL)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(03460)
+ "Error while compressing data");
+ return APR_EGENERAL;
+ }
+
+ if (!BrotliEncoderHasMoreOutput(ctx->state)) {
+ break;
+ }
+
+ /* A flush can require several calls to BrotliEncoderCompressStream(),
+ * so place the data on the heap (otherwise, the pointer will become
+ * invalid after the next call to BrotliEncoderCompressStream()).
+ */
+ output_len = 0;
+ output = BrotliEncoderTakeOutput(ctx->state, &output_len);
+ ctx->total_out += output_len;
+
+ b = apr_bucket_heap_create((const char *)output, output_len, NULL,
+ ctx->bb->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
+ }
+
+ return APR_SUCCESS;
+}
+
+static const char *get_content_encoding(request_rec *r)
+{
+ const char *encoding;
+
+ 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;
+ }
+
+ return encoding;
+}
+
+static apr_status_t compress_filter(ap_filter_t *f, apr_bucket_brigade *bb)
+{
+ request_rec *r = f->r;
+ brotli_ctx_t *ctx = f->ctx;
+ apr_status_t rv;
+ brotli_server_config_t *conf;
+
+ if (APR_BRIGADE_EMPTY(bb)) {
+ return APR_SUCCESS;
+ }
+
+ conf = ap_get_module_config(r->server->module_config, &brotli_module);
+
+ if (!ctx) {
+ const char *encoding;
+ const char *token;
+ const char *accepts;
+ const char *q = NULL;
+
+ /* Only work on main request, not subrequests, that are not
+ * a 204 response with no content, and are not tagged with the
+ * no-brotli env variable, and are not a partial response to
+ * a Range request.
+ *
+ * Note that responding to 304 is handled separately to set
+ * the required headers (such as ETag) per RFC7232, 4.1.
+ */
+ if (r->main || r->status == HTTP_NO_CONTENT
+ || apr_table_get(r->subprocess_env, "no-brotli")
+ || apr_table_get(r->headers_out, "Content-Range")) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ /* Let's see what our current Content-Encoding is. */
+ encoding = get_content_encoding(r);
+
+ if (encoding) {
+ const char *tmp = encoding;
+
+ token = ap_get_token(r->pool, &tmp, 0);
+ while (token && *token) {
+ if (strcmp(token, "identity") != 0 &&
+ strcmp(token, "7bit") != 0 &&
+ strcmp(token, "8bit") != 0 &&
+ strcmp(token, "binary") != 0) {
+ /* The data is already encoded, do nothing. */
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ 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");
+
+ accepts = apr_table_get(r->headers_in, "Accept-Encoding");
+ if (!accepts) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ /* Do we have Accept-Encoding: br? */
+ token = ap_get_token(r->pool, &accepts, 0);
+ while (token && token[0] && ap_cstr_casecmp(token, "br") != 0) {
+ while (*accepts == ';') {
+ ++accepts;
+ ap_get_token(r->pool, &accepts, 1);
+ }
+
+ if (*accepts == ',') {
+ ++accepts;
+ }
+ token = (*accepts) ? ap_get_token(r->pool, &accepts, 0) : NULL;
+ }
+
+ /* Find the qvalue, if provided */
+ if (*accepts) {
+ while (*accepts == ';') {
+ ++accepts;
+ }
+ q = ap_get_token(r->pool, &accepts, 1);
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+ "token: '%s' - q: '%s'", token ? token : "NULL", q);
+ }
+
+ /* No acceptable token found or q=0 */
+ if (!token || token[0] == '\0' ||
+ (q && strlen(q) >= 3 && strncmp("q=0.000", q, strlen(q)) == 0)) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ /* If the entire Content-Encoding is "identity", we can replace it. */
+ if (!encoding || ap_cstr_casecmp(encoding, "identity") == 0) {
+ apr_table_setn(r->headers_out, "Content-Encoding", "br");
+ } else {
+ apr_table_mergen(r->headers_out, "Content-Encoding", "br");
+ }
+
+ if (r->content_encoding) {
+ r->content_encoding = apr_table_get(r->headers_out,
+ "Content-Encoding");
+ }
+
+ apr_table_unset(r->headers_out, "Content-Length");
+ apr_table_unset(r->headers_out, "Content-MD5");
+
+ /* https://bz.apache.org/bugzilla/show_bug.cgi?id=39727
+ * https://bz.apache.org/bugzilla/show_bug.cgi?id=45023
+ *
+ * ETag must be unique among the possible representations, so a
+ * change to content-encoding requires a corresponding change to the
+ * ETag. We make this behavior configurable, and mimic mod_deflate's
+ * DeflateAlterETag with BrotliAlterETag to keep the transition from
+ * mod_deflate seamless.
+ */
+ if (conf->etag_mode == ETAG_MODE_REMOVE) {
+ apr_table_unset(r->headers_out, "ETag");
+ }
+ else if (conf->etag_mode == ETAG_MODE_ADDSUFFIX) {
+ const char *etag = apr_table_get(r->headers_out, "ETag");
+
+ if (etag) {
+ apr_size_t len = strlen(etag);
+
+ if (len > 2 && etag[len - 1] == '"') {
+ etag = apr_pstrmemdup(r->pool, etag, len - 1);
+ etag = apr_pstrcat(r->pool, etag, "-br\"", NULL);
+ apr_table_setn(r->headers_out, "ETag", etag);
+ }
+ }
+ }
+
+ /* For 304 responses, we only need to send out the headers. */
+ if (r->status == HTTP_NOT_MODIFIED) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ ctx = create_ctx(conf->quality, conf->lgwin, conf->lgblock,
+ f->c->bucket_alloc, r->pool);
+ f->ctx = ctx;
+ }
+
+ while (!APR_BRIGADE_EMPTY(bb)) {
+ apr_bucket *e = APR_BRIGADE_FIRST(bb);
+
+ /* Optimization: If we are a HEAD request and bytes_sent is not zero
+ * it means that we have passed the content-length filter once and
+ * have more data to send. This means that the content-length filter
+ * could not determine our content-length for the response to the
+ * HEAD request anyway (the associated GET request would deliver the
+ * body in chunked encoding) and we can stop compressing.
+ */
+ if (r->header_only && r->bytes_sent) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ if (APR_BUCKET_IS_EOS(e)) {
+ rv = flush(ctx, BROTLI_OPERATION_FINISH, f);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ /* Leave notes for logging. */
+ if (conf->note_input_name) {
+ apr_table_setn(r->notes, conf->note_input_name,
+ apr_off_t_toa(r->pool, ctx->total_in));
+ }
+ if (conf->note_output_name) {
+ apr_table_setn(r->notes, conf->note_output_name,
+ apr_off_t_toa(r->pool, ctx->total_out));
+ }
+ if (conf->note_ratio_name) {
+ if (ctx->total_in > 0) {
+ int ratio = (int) (ctx->total_out * 100 / ctx->total_in);
+
+ apr_table_setn(r->notes, conf->note_ratio_name,
+ apr_itoa(r->pool, ratio));
+ }
+ else {
+ apr_table_setn(r->notes, conf->note_ratio_name, "-");
+ }
+ }
+
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+
+ rv = ap_pass_brigade(f->next, ctx->bb);
+ apr_brigade_cleanup(ctx->bb);
+ apr_pool_cleanup_run(r->pool, ctx, cleanup_ctx);
+ return rv;
+ }
+ else if (APR_BUCKET_IS_FLUSH(e)) {
+ rv = flush(ctx, BROTLI_OPERATION_FLUSH, f);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+
+ rv = ap_pass_brigade(f->next, ctx->bb);
+ apr_brigade_cleanup(ctx->bb);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ }
+ else if (APR_BUCKET_IS_METADATA(e)) {
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+ }
+ else {
+ const char *data;
+ apr_size_t len;
+
+ rv = apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ rv = process_chunk(ctx, data, len, f);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ apr_bucket_delete(e);
+ }
+ }
+ return APR_SUCCESS;
+}
+
+static void register_hooks(apr_pool_t *p)
+{
+ ap_register_output_filter("BROTLI_COMPRESS", compress_filter, NULL,
+ AP_FTYPE_CONTENT_SET);
+}
+
+static const command_rec cmds[] = {
+ AP_INIT_TAKE12("BrotliFilterNote", set_filter_note,
+ NULL, RSRC_CONF,
+ "Set a note to report on compression ratio"),
+ AP_INIT_TAKE1("BrotliCompressionQuality", set_compression_quality,
+ NULL, RSRC_CONF,
+ "Compression quality between 0 and 11 (higher quality means "
+ "slower compression)"),
+ AP_INIT_TAKE1("BrotliCompressionWindow", set_compression_lgwin,
+ NULL, RSRC_CONF,
+ "Sliding window size between 10 and 24 (larger windows can "
+ "improve compression, but require more memory)"),
+ AP_INIT_TAKE1("BrotliCompressionMaxInputBlock", set_compression_lgblock,
+ NULL, RSRC_CONF,
+ "Maximum input block size between 16 and 24 (larger block "
+ "sizes require more memory)"),
+ AP_INIT_TAKE1("BrotliAlterETag", set_etag_mode,
+ NULL, RSRC_CONF,
+ "Set how mod_brotli should modify ETag response headers: "
+ "'AddSuffix' (default), 'NoChange', 'Remove'"),
+ {NULL}
+};
+
+AP_DECLARE_MODULE(brotli) = {
+ STANDARD20_MODULE_STUFF,
+ NULL, /* create per-directory config structure */
+ NULL, /* merge per-directory config structures */
+ create_server_config, /* create per-server config structure */
+ NULL, /* merge per-server config structures */
+ cmds, /* command apr_table_t */
+ register_hooks /* register hooks */
+};
diff --git a/modules/filters/mod_brotli.dep b/modules/filters/mod_brotli.dep
new file mode 100644
index 0000000..adafc46
--- /dev/null
+++ b/modules/filters/mod_brotli.dep
@@ -0,0 +1,45 @@
+# Microsoft Developer Studio Generated Dependency File, included by mod_brotli.mak
+
+..\..\build\win32\httpd.rc : \
+ "..\..\include\ap_release.h"\
+
+
+.\mod_brotli.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+
diff --git a/modules/filters/mod_brotli.dsp b/modules/filters/mod_brotli.dsp
new file mode 100644
index 0000000..8576e95
--- /dev/null
+++ b/modules/filters/mod_brotli.dsp
@@ -0,0 +1,111 @@
+# Microsoft Developer Studio Project File - Name="mod_brotli" - 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_brotli - 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_brotli.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_brotli.mak" CFG="mod_brotli - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_brotli - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_brotli - 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_brotli - 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/brotli/include" /I "../../srclib/brotli/c/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_brotli_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 /fo"Release/mod_brotli.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_brotli.so" /d LONG_NAME="brotli_module for Apache"
+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_brotli.so" /libpath:"../../srclib/brotli" /base:@..\..\os\win32\BaseAddr.ref,mod_brotli.so
+# ADD LINK32 kernel32.lib brotlicommon.lib brotlienc.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_brotli.so" /libpath:"../../srclib/brotli" /base:@..\..\os\win32\BaseAddr.ref,mod_brotli.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_brotli.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "mod_brotli - 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/brotli/include" /I "../../srclib/brotli/c/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "HAVE_ZUTIL_H" /Fd"Debug\mod_brotli_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 /fo"Debug/mod_brotli.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_brotli.so" /d LONG_NAME="brotli_module for Apache"
+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_brotli.so" /libpath:"../../srclib/brotli" /base:@..\..\os\win32\BaseAddr.ref,mod_brotli.so
+# ADD LINK32 kernel32.lib brotlicommon.lib brotlienc.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_brotli.so" /libpath:"../../srclib/brotli" /base:@..\..\os\win32\BaseAddr.ref,mod_brotli.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_brotli.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_brotli - Win32 Release"
+# Name "mod_brotli - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\mod_brotli.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
diff --git a/modules/filters/mod_brotli.mak b/modules/filters/mod_brotli.mak
new file mode 100644
index 0000000..79ec9da
--- /dev/null
+++ b/modules/filters/mod_brotli.mak
@@ -0,0 +1,353 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on mod_brotli.dsp
+!IF "$(CFG)" == ""
+CFG=mod_brotli - Win32 Release
+!MESSAGE No configuration specified. Defaulting to mod_brotli - Win32 Release.
+!ENDIF
+
+!IF "$(CFG)" != "mod_brotli - Win32 Release" && "$(CFG)" != "mod_brotli - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!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_brotli.mak" CFG="mod_brotli - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_brotli - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_brotli - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(CFG)" == "mod_brotli - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_brotli.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_brotli.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_brotli.obj"
+ -@erase "$(INTDIR)\mod_brotli.res"
+ -@erase "$(INTDIR)\mod_brotli_src.idb"
+ -@erase "$(INTDIR)\mod_brotli_src.pdb"
+ -@erase "$(OUTDIR)\mod_brotli.exp"
+ -@erase "$(OUTDIR)\mod_brotli.lib"
+ -@erase "$(OUTDIR)\mod_brotli.pdb"
+ -@erase "$(OUTDIR)\mod_brotli.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/brotli/include" /I "../../srclib/brotli/c/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_brotli_src" /FD /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_brotli.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_brotli.so" /d LONG_NAME="brotli_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_brotli.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib brotlicommon.lib brotlienc.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_brotli.pdb" /libpath:"../../srclib/brotli" /debug /out:"$(OUTDIR)\mod_brotli.so" /implib:"$(OUTDIR)\mod_brotli.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_brotli.so /opt:ref
+LINK32_OBJS= \
+ "$(INTDIR)\mod_brotli.obj" \
+ "$(INTDIR)\mod_brotli.res" \
+ "..\..\srclib\apr\Release\libapr-1.lib" \
+ "..\..\srclib\apr-util\Release\libaprutil-1.lib" \
+ "..\..\Release\libhttpd.lib"
+
+"$(OUTDIR)\mod_brotli.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Release\mod_brotli.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_brotli.so"
+ if exist .\Release\mod_brotli.so.manifest mt.exe -manifest .\Release\mod_brotli.so.manifest -outputresource:.\Release\mod_brotli.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ELSEIF "$(CFG)" == "mod_brotli - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_brotli.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_brotli.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_brotli.obj"
+ -@erase "$(INTDIR)\mod_brotli.res"
+ -@erase "$(INTDIR)\mod_brotli_src.idb"
+ -@erase "$(INTDIR)\mod_brotli_src.pdb"
+ -@erase "$(OUTDIR)\mod_brotli.exp"
+ -@erase "$(OUTDIR)\mod_brotli.lib"
+ -@erase "$(OUTDIR)\mod_brotli.pdb"
+ -@erase "$(OUTDIR)\mod_brotli.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/brotli/include" /I "../../srclib/brotli/c/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "HAVE_ZUTIL_H" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_brotli_src" /FD /EHsc /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_brotli.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_brotli.so" /d LONG_NAME="brotli_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_brotli.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib brotlicommon.lib brotlienc.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_brotli.pdb" /debug /libpath:"../../srclib/brotli" /out:"$(OUTDIR)\mod_brotli.so" /implib:"$(OUTDIR)\mod_brotli.lib" /libpath:"..\..\srclib\brotli\Debug\bin" /base:@..\..\os\win32\BaseAddr.ref,mod_brotli.so
+LINK32_OBJS= \
+ "$(INTDIR)\mod_brotli.obj" \
+ "$(INTDIR)\mod_brotli.res" \
+ "..\..\srclib\apr\Debug\libapr-1.lib" \
+ "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \
+ "..\..\Debug\libhttpd.lib"
+
+"$(OUTDIR)\mod_brotli.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Debug\mod_brotli.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_brotli.so"
+ if exist .\Debug\mod_brotli.so.manifest mt.exe -manifest .\Debug\mod_brotli.so.manifest -outputresource:.\Debug\mod_brotli.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+
+!IF "$(NO_EXTERNAL_DEPS)" != "1"
+!IF EXISTS("mod_brotli.dep")
+!INCLUDE "mod_brotli.dep"
+!ELSE
+!MESSAGE Warning: cannot find "mod_brotli.dep"
+!ENDIF
+!ENDIF
+
+
+!IF "$(CFG)" == "mod_brotli - Win32 Release" || "$(CFG)" == "mod_brotli - Win32 Debug"
+
+!IF "$(CFG)" == "mod_brotli - Win32 Release"
+
+"libapr - Win32 Release" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_brotli - Win32 Debug"
+
+"libapr - Win32 Debug" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_brotli - Win32 Release"
+
+"libaprutil - Win32 Release" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_brotli - Win32 Debug"
+
+"libaprutil - Win32 Debug" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_brotli - Win32 Release"
+
+"libhttpd - Win32 Release" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 ReleaseCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_brotli - Win32 Debug"
+
+"libhttpd - Win32 Debug" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 DebugCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ENDIF
+
+SOURCE=..\..\build\win32\httpd.rc
+
+!IF "$(CFG)" == "mod_brotli - Win32 Release"
+
+
+"$(INTDIR)\mod_brotli.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_brotli.res" /i "../../include" /i "../../srclib/apr/include" /i "\build4\httpd-2.4.23\build\win32" /d "NDEBUG" /d BIN_NAME="mod_brotli.so" /d LONG_NAME="brotli_module for Apache" $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "mod_brotli - Win32 Debug"
+
+
+"$(INTDIR)\mod_brotli.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_brotli.res" /i "../../include" /i "../../srclib/apr/include" /i "\build4\httpd-2.4.23\build\win32" /d "_DEBUG" /d BIN_NAME="mod_brotli.so" /d LONG_NAME="brotli_module for Apache" $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\mod_brotli.c
+
+"$(INTDIR)\mod_brotli.obj" : $(SOURCE) "$(INTDIR)"
+
+
+
+!ENDIF
+
diff --git a/modules/filters/mod_buffer.c b/modules/filters/mod_buffer.c
new file mode 100644
index 0000000..203e672
--- /dev/null
+++ b/modules/filters/mod_buffer.c
@@ -0,0 +1,353 @@
+/* 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_buffer.c --- Buffer the input and output filter stacks, collapse
+ * many small buckets into fewer large buckets.
+ */
+
+#include "apr.h"
+#include "apr_strings.h"
+#include "apr_buckets.h"
+#include "apr_lib.h"
+
+#include "ap_config.h"
+#include "util_filter.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_log.h"
+#include "http_request.h"
+
+static const char bufferFilterName[] = "BUFFER";
+module AP_MODULE_DECLARE_DATA buffer_module;
+
+#define DEFAULT_BUFFER_SIZE 128*1024
+
+typedef struct buffer_conf {
+ apr_off_t size; /* size of the buffer */
+ int size_set; /* has the size been set */
+} buffer_conf;
+
+typedef struct buffer_ctx {
+ apr_bucket_brigade *bb;
+ apr_bucket_brigade *tmp;
+ buffer_conf *conf;
+ apr_off_t remaining;
+ int seen_eos;
+} buffer_ctx;
+
+/**
+ * Buffer buckets being written to the output filter stack.
+ */
+static apr_status_t buffer_out_filter(ap_filter_t *f, apr_bucket_brigade *bb)
+{
+ apr_bucket *e;
+ request_rec *r = f->r;
+ buffer_ctx *ctx = f->ctx;
+ apr_status_t rv = APR_SUCCESS;
+ int move = 0;
+
+ /* first time in? create a context */
+ if (!ctx) {
+
+ /* buffering won't work on subrequests, it would be nice if
+ * it did. Within subrequests, we have no EOS to check for,
+ * so we don't know when to flush the buffer to the network
+ */
+ if (f->r->main) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
+ ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
+ ctx->conf = ap_get_module_config(f->r->per_dir_config, &buffer_module);
+ }
+
+ /* Do nothing if asked to filter nothing. */
+ if (APR_BRIGADE_EMPTY(bb)) {
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ /* Empty buffer means we can potentially optimise below */
+ if (APR_BRIGADE_EMPTY(ctx->bb)) {
+ move = 1;
+ }
+
+ while (APR_SUCCESS == rv && !APR_BRIGADE_EMPTY(bb)) {
+ const char *data;
+ apr_off_t len;
+ apr_size_t size;
+
+ e = APR_BRIGADE_FIRST(bb);
+
+ /* EOS means we are done. */
+ if (APR_BUCKET_IS_EOS(e)) {
+
+ /* should we add an etag? */
+
+ /* pass the EOS across */
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+
+ /* pass what we have down the chain */
+ rv = ap_pass_brigade(f->next, ctx->bb);
+ continue;
+ }
+
+ /* A flush takes precedence over buffering */
+ if (APR_BUCKET_IS_FLUSH(e)) {
+
+ /* pass the flush bucket across */
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+
+ /* pass what we have down the chain */
+ rv = ap_pass_brigade(f->next, ctx->bb);
+ continue;
+ }
+
+ /* metadata buckets are preserved as is */
+ if (APR_BUCKET_IS_METADATA(e)) {
+ /*
+ * Remove meta data bucket from old brigade and insert into the
+ * new.
+ */
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+ continue;
+ }
+
+ /* is our buffer full?
+ * If so, send what we have down the filter chain. If the buffer
+ * gets full, we can no longer compute a content length.
+ */
+ apr_brigade_length(ctx->bb, 1, &len);
+ if (len > ctx->conf->size) {
+
+ /* pass what we have down the chain */
+ rv = ap_pass_brigade(f->next, ctx->bb);
+ if (rv) {
+ /* should break out of the loop, since our write to the client
+ * failed in some way. */
+ continue;
+ }
+ }
+
+ /* at this point we are ready to buffer.
+ * Buffering takes advantage of an optimisation in the handling of
+ * bucket brigades. Heap buckets are always created at a fixed
+ * size, regardless of the size of the data placed into them.
+ * The apr_brigade_write() call will first try and pack the data
+ * into any free space in the most recent heap bucket, before
+ * allocating a new bucket if necessary.
+ */
+ if (APR_SUCCESS == (rv = apr_bucket_read(e, &data, &size,
+ APR_BLOCK_READ))) {
+
+ /* further optimisation: if the buckets are already heap
+ * buckets, and the buckets stay exactly APR_BUCKET_BUFF_SIZE
+ * long (as they would be if we were reading bits of a
+ * large bucket), then move the buckets instead of copying
+ * them.
+ */
+ if (move && APR_BUCKET_IS_HEAP(e)) {
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+ if (APR_BUCKET_BUFF_SIZE != size) {
+ move = 0;
+ }
+ } else {
+ apr_brigade_write(ctx->bb, NULL, NULL, data, size);
+ apr_bucket_delete(e);
+ }
+
+ }
+
+ }
+
+ return rv;
+
+}
+
+/**
+ * Buffer buckets being read from the input filter stack.
+ */
+static apr_status_t buffer_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 *e, *after;
+ apr_status_t rv;
+ buffer_ctx *ctx = f->ctx;
+
+ /* buffer on main requests only */
+ if (!ap_is_initial_req(f->r)) {
+ ap_remove_input_filter(f);
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
+
+ /* first time in? create a context */
+ if (!ctx) {
+ ctx = f->ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
+ ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+ ctx->tmp = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+ ctx->conf = ap_get_module_config(f->r->per_dir_config, &buffer_module);
+ }
+
+ /* 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);
+ }
+
+ /* if our buffer is empty, read off the network until the buffer is full */
+ if (APR_BRIGADE_EMPTY(ctx->bb)) {
+ int seen_flush = 0;
+
+ ctx->remaining = ctx->conf->size;
+
+ while (!ctx->seen_eos && !seen_flush && ctx->remaining > 0) {
+ const char *data;
+ apr_size_t size = 0;
+
+ if (APR_BRIGADE_EMPTY(ctx->tmp)) {
+ rv = ap_get_brigade(f->next, ctx->tmp, mode, block,
+ ctx->remaining);
+
+ /* if an error was received, bail out now. If the error is
+ * EAGAIN and we have not yet seen an EOS, we will definitely
+ * be called again, at which point we will send our buffered
+ * data. Instead of sending EAGAIN, some filters return an
+ * empty brigade instead when data is not yet available. In
+ * this case, pass through the APR_SUCCESS and emulate the
+ * underlying filter.
+ */
+ if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(ctx->tmp)) {
+ return rv;
+ }
+ }
+
+ do {
+ e = APR_BRIGADE_FIRST(ctx->tmp);
+
+ /* if we see an EOS, we are done */
+ if (APR_BUCKET_IS_EOS(e)) {
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+ ctx->seen_eos = 1;
+ break;
+ }
+
+ /* flush buckets clear the buffer */
+ if (APR_BUCKET_IS_FLUSH(e)) {
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+ seen_flush = 1;
+ break;
+ }
+
+ /* pass metadata buckets through */
+ if (APR_BUCKET_IS_METADATA(e)) {
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+ continue;
+ }
+
+ /* read the bucket in, pack it into the buffer */
+ if (APR_SUCCESS == (rv = apr_bucket_read(e, &data, &size,
+ APR_BLOCK_READ))) {
+ apr_brigade_write(ctx->bb, NULL, NULL, data, size);
+ ctx->remaining -= size;
+ apr_bucket_delete(e);
+ } else {
+ return rv;
+ }
+
+ } while (!APR_BRIGADE_EMPTY(ctx->tmp));
+ }
+ }
+
+ /* give the caller the data they asked for from the buffer */
+ apr_brigade_partition(ctx->bb, readbytes, &after);
+ e = APR_BRIGADE_FIRST(ctx->bb);
+ while (e != after) {
+ if (APR_BUCKET_IS_EOS(e)) {
+ /* last bucket read, step out of the way */
+ ap_remove_input_filter(f);
+ }
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(bb, e);
+ e = APR_BRIGADE_FIRST(ctx->bb);
+ }
+
+ return APR_SUCCESS;
+}
+
+static void *create_buffer_config(apr_pool_t *p, char *dummy)
+{
+ buffer_conf *new = (buffer_conf *) apr_pcalloc(p, sizeof(buffer_conf));
+
+ new->size_set = 0; /* unset */
+ new->size = DEFAULT_BUFFER_SIZE; /* default size */
+
+ return (void *) new;
+}
+
+static void *merge_buffer_config(apr_pool_t *p, void *basev, void *addv)
+{
+ buffer_conf *new = (buffer_conf *) apr_pcalloc(p, sizeof(buffer_conf));
+ buffer_conf *add = (buffer_conf *) addv;
+ buffer_conf *base = (buffer_conf *) basev;
+
+ new->size = (add->size_set == 0) ? base->size : add->size;
+ new->size_set = add->size_set || base->size_set;
+
+ return new;
+}
+
+static const char *set_buffer_size(cmd_parms *cmd, void *dconf, const char *arg)
+{
+ buffer_conf *conf = dconf;
+
+ if (APR_SUCCESS != apr_strtoff(&(conf->size), arg, NULL, 10) || conf->size
+ <= 0) {
+ return "BufferSize must be a size in bytes, and greater than zero";
+ }
+ conf->size_set = 1;
+
+ return NULL;
+}
+
+static const command_rec buffer_cmds[] = { AP_INIT_TAKE1("BufferSize",
+ set_buffer_size, NULL, ACCESS_CONF,
+ "Maximum size of the buffer used by the buffer filter"), { NULL } };
+
+static void register_hooks(apr_pool_t *p)
+{
+ ap_register_output_filter(bufferFilterName, buffer_out_filter, NULL,
+ AP_FTYPE_CONTENT_SET);
+ ap_register_input_filter(bufferFilterName, buffer_in_filter, NULL,
+ AP_FTYPE_CONTENT_SET);
+}
+
+AP_DECLARE_MODULE(buffer) = {
+ STANDARD20_MODULE_STUFF,
+ create_buffer_config, /* create per-directory config structure */
+ merge_buffer_config, /* merge per-directory config structures */
+ NULL, /* create per-server config structure */
+ NULL, /* merge per-server config structures */
+ buffer_cmds, /* command apr_table_t */
+ register_hooks /* register hooks */
+};
diff --git a/modules/filters/mod_buffer.dep b/modules/filters/mod_buffer.dep
new file mode 100644
index 0000000..6f77613
--- /dev/null
+++ b/modules/filters/mod_buffer.dep
@@ -0,0 +1,48 @@
+# Microsoft Developer Studio Generated Dependency File, included by mod_buffer.mak
+
+..\..\build\win32\httpd.rc : \
+ "..\..\include\ap_release.h"\
+
+
+.\mod_buffer.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+
diff --git a/modules/filters/mod_buffer.dsp b/modules/filters/mod_buffer.dsp
new file mode 100644
index 0000000..26834f5
--- /dev/null
+++ b/modules/filters/mod_buffer.dsp
@@ -0,0 +1,111 @@
+# Microsoft Developer Studio Project File - Name="mod_buffer" - 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_buffer - 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_buffer.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_buffer.mak" CFG="mod_buffer - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_buffer - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_buffer - 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_buffer - 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" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_buffer_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 /fo"Release/mod_buffer.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_buffer.so" /d LONG_NAME="buffer_module for Apache"
+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_buffer.so" /base:@..\..\os\win32\BaseAddr.ref,mod_buffer.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_buffer.so" /base:@..\..\os\win32\BaseAddr.ref,mod_buffer.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_buffer.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "mod_buffer - 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" /D "HAVE_ZUTIL_H" /Fd"Debug\mod_buffer_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 /fo"Debug/mod_buffer.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_buffer.so" /d LONG_NAME="buffer_module for Apache"
+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_buffer.so" /base:@..\..\os\win32\BaseAddr.ref,mod_buffer.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_buffer.so" /base:@..\..\os\win32\BaseAddr.ref,mod_buffer.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_buffer.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_buffer - Win32 Release"
+# Name "mod_buffer - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\mod_buffer.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
diff --git a/modules/filters/mod_buffer.mak b/modules/filters/mod_buffer.mak
new file mode 100644
index 0000000..d74bec2
--- /dev/null
+++ b/modules/filters/mod_buffer.mak
@@ -0,0 +1,353 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on mod_buffer.dsp
+!IF "$(CFG)" == ""
+CFG=mod_buffer - Win32 Release
+!MESSAGE No configuration specified. Defaulting to mod_buffer - Win32 Release.
+!ENDIF
+
+!IF "$(CFG)" != "mod_buffer - Win32 Release" && "$(CFG)" != "mod_buffer - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!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_buffer.mak" CFG="mod_buffer - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_buffer - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_buffer - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(CFG)" == "mod_buffer - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_buffer.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_buffer.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_buffer.obj"
+ -@erase "$(INTDIR)\mod_buffer.res"
+ -@erase "$(INTDIR)\mod_buffer_src.idb"
+ -@erase "$(INTDIR)\mod_buffer_src.pdb"
+ -@erase "$(OUTDIR)\mod_buffer.exp"
+ -@erase "$(OUTDIR)\mod_buffer.lib"
+ -@erase "$(OUTDIR)\mod_buffer.pdb"
+ -@erase "$(OUTDIR)\mod_buffer.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_buffer_src" /FD /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_buffer.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_buffer.so" /d LONG_NAME="buffer_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_buffer.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_buffer.pdb" /debug /out:"$(OUTDIR)\mod_buffer.so" /implib:"$(OUTDIR)\mod_buffer.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_buffer.so /opt:ref
+LINK32_OBJS= \
+ "$(INTDIR)\mod_buffer.obj" \
+ "$(INTDIR)\mod_buffer.res" \
+ "..\..\srclib\apr\Release\libapr-1.lib" \
+ "..\..\srclib\apr-util\Release\libaprutil-1.lib" \
+ "..\..\Release\libhttpd.lib"
+
+"$(OUTDIR)\mod_buffer.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Release\mod_buffer.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_buffer.so"
+ if exist .\Release\mod_buffer.so.manifest mt.exe -manifest .\Release\mod_buffer.so.manifest -outputresource:.\Release\mod_buffer.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ELSEIF "$(CFG)" == "mod_buffer - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_buffer.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_buffer.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_buffer.obj"
+ -@erase "$(INTDIR)\mod_buffer.res"
+ -@erase "$(INTDIR)\mod_buffer_src.idb"
+ -@erase "$(INTDIR)\mod_buffer_src.pdb"
+ -@erase "$(OUTDIR)\mod_buffer.exp"
+ -@erase "$(OUTDIR)\mod_buffer.lib"
+ -@erase "$(OUTDIR)\mod_buffer.pdb"
+ -@erase "$(OUTDIR)\mod_buffer.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "HAVE_ZUTIL_H" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_buffer_src" /FD /EHsc /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_buffer.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_buffer.so" /d LONG_NAME="buffer_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_buffer.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_buffer.pdb" /debug /out:"$(OUTDIR)\mod_buffer.so" /implib:"$(OUTDIR)\mod_buffer.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_buffer.so
+LINK32_OBJS= \
+ "$(INTDIR)\mod_buffer.obj" \
+ "$(INTDIR)\mod_buffer.res" \
+ "..\..\srclib\apr\Debug\libapr-1.lib" \
+ "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \
+ "..\..\Debug\libhttpd.lib"
+
+"$(OUTDIR)\mod_buffer.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Debug\mod_buffer.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_buffer.so"
+ if exist .\Debug\mod_buffer.so.manifest mt.exe -manifest .\Debug\mod_buffer.so.manifest -outputresource:.\Debug\mod_buffer.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+
+!IF "$(NO_EXTERNAL_DEPS)" != "1"
+!IF EXISTS("mod_buffer.dep")
+!INCLUDE "mod_buffer.dep"
+!ELSE
+!MESSAGE Warning: cannot find "mod_buffer.dep"
+!ENDIF
+!ENDIF
+
+
+!IF "$(CFG)" == "mod_buffer - Win32 Release" || "$(CFG)" == "mod_buffer - Win32 Debug"
+
+!IF "$(CFG)" == "mod_buffer - Win32 Release"
+
+"libapr - Win32 Release" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_buffer - Win32 Debug"
+
+"libapr - Win32 Debug" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_buffer - Win32 Release"
+
+"libaprutil - Win32 Release" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_buffer - Win32 Debug"
+
+"libaprutil - Win32 Debug" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_buffer - Win32 Release"
+
+"libhttpd - Win32 Release" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 ReleaseCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_buffer - Win32 Debug"
+
+"libhttpd - Win32 Debug" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 DebugCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ENDIF
+
+SOURCE=..\..\build\win32\httpd.rc
+
+!IF "$(CFG)" == "mod_buffer - Win32 Release"
+
+
+"$(INTDIR)\mod_buffer.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_buffer.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_buffer.so" /d LONG_NAME="buffer_module for Apache" $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "mod_buffer - Win32 Debug"
+
+
+"$(INTDIR)\mod_buffer.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_buffer.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_buffer.so" /d LONG_NAME="buffer_module for Apache" $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\mod_buffer.c
+
+"$(INTDIR)\mod_buffer.obj" : $(SOURCE) "$(INTDIR)"
+
+
+
+!ENDIF
+
diff --git a/modules/filters/mod_charset_lite.c b/modules/filters/mod_charset_lite.c
new file mode 100644
index 0000000..e3d1ce9
--- /dev/null
+++ b/modules/filters/mod_charset_lite.c
@@ -0,0 +1,1142 @@
+/* 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.
+ */
+
+/*
+ * simple hokey charset recoding configuration module
+ *
+ * See mod_ebcdic and mod_charset for more thought-out examples. This
+ * one is just so Jeff can learn how a module works and experiment with
+ * basic character set recoding configuration.
+ *
+ * !!!This is an extremely cheap ripoff of mod_charset.c from Russian Apache!!!
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+
+#include "http_core.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "util_charset.h"
+#include "apr_buckets.h"
+#include "util_filter.h"
+#include "apr_strings.h"
+#include "apr_lib.h"
+#include "apr_xlate.h"
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+
+#define OUTPUT_XLATE_BUF_SIZE (16*1024) /* size of translation buffer used on output */
+#define INPUT_XLATE_BUF_SIZE (8*1024) /* size of translation buffer used on input */
+
+#define XLATE_MIN_BUFF_LEFT 128 /* flush once there is no more than this much
+ * space left in the translation buffer
+ */
+
+#define FATTEST_CHAR 8 /* we don't handle chars wider than this that straddle
+ * two buckets
+ */
+
+/* extended error status codes; this is used in addition to an apr_status_t to
+ * track errors in the translation filter
+ */
+typedef enum {
+ EES_INIT = 0, /* no error info yet; value must be 0 for easy init */
+ EES_LIMIT, /* built-in restriction encountered */
+ EES_INCOMPLETE_CHAR, /* incomplete multi-byte char at end of content */
+ EES_BUCKET_READ,
+ EES_DOWNSTREAM, /* something bad happened in a filter below xlate */
+ EES_BAD_INPUT /* input data invalid */
+} ees_t;
+
+/* registered name of the output translation filter */
+#define XLATEOUT_FILTER_NAME "XLATEOUT"
+/* registered name of input translation filter */
+#define XLATEIN_FILTER_NAME "XLATEIN"
+
+typedef struct charset_dir_t {
+ const char *charset_source; /* source encoding */
+ const char *charset_default; /* how to ship on wire */
+ /** module does ap_add_*_filter()? */
+ enum {IA_INIT, IA_IMPADD, IA_NOIMPADD} implicit_add;
+ /** treat all mimetypes as text? */
+ enum {FX_INIT, FX_FORCE, FX_NOFORCE} force_xlate;
+} charset_dir_t;
+
+/* charset_filter_ctx_t is created for each filter instance; because the same
+ * filter code is used for translating in both directions, we need this context
+ * data to tell the filter which translation handle to use; it also can hold a
+ * character which was split between buckets
+ */
+typedef struct charset_filter_ctx_t {
+ apr_xlate_t *xlate;
+ int is_sb; /* single-byte translation? */
+ charset_dir_t *dc;
+ ees_t ees; /* extended error status */
+ apr_size_t saved;
+ char buf[FATTEST_CHAR]; /* we want to be able to build a complete char here */
+ int ran; /* has filter instance run before? */
+ int noop; /* should we pass brigades through unchanged? */
+ char *tmp; /* buffer for input filtering */
+ apr_bucket_brigade *bb; /* input buckets we couldn't finish translating */
+ apr_bucket_brigade *tmpbb; /* used for passing downstream */
+} charset_filter_ctx_t;
+
+/* charset_req_t is available via r->request_config if any translation is
+ * being performed
+ */
+typedef struct charset_req_t {
+ charset_dir_t *dc;
+ charset_filter_ctx_t *output_ctx, *input_ctx;
+} charset_req_t;
+
+module AP_MODULE_DECLARE_DATA charset_lite_module;
+
+static void *create_charset_dir_conf(apr_pool_t *p,char *dummy)
+{
+ charset_dir_t *dc = (charset_dir_t *)apr_pcalloc(p,sizeof(charset_dir_t));
+
+ return dc;
+}
+
+static void *merge_charset_dir_conf(apr_pool_t *p, void *basev, void *overridesv)
+{
+ charset_dir_t *a = (charset_dir_t *)apr_pcalloc (p, sizeof(charset_dir_t));
+ charset_dir_t *base = (charset_dir_t *)basev,
+ *over = (charset_dir_t *)overridesv;
+
+ /* If it is defined in the current container, use it. Otherwise, use the one
+ * from the enclosing container.
+ */
+
+ a->charset_default =
+ over->charset_default ? over->charset_default : base->charset_default;
+ a->charset_source =
+ over->charset_source ? over->charset_source : base->charset_source;
+ a->implicit_add =
+ over->implicit_add != IA_INIT ? over->implicit_add : base->implicit_add;
+ a->force_xlate=
+ over->force_xlate != FX_INIT ? over->force_xlate : base->force_xlate;
+ return a;
+}
+
+/* CharsetSourceEnc charset
+ */
+static const char *add_charset_source(cmd_parms *cmd, void *in_dc,
+ const char *name)
+{
+ charset_dir_t *dc = in_dc;
+
+ dc->charset_source = name;
+ return NULL;
+}
+
+/* CharsetDefault charset
+ */
+static const char *add_charset_default(cmd_parms *cmd, void *in_dc,
+ const char *name)
+{
+ charset_dir_t *dc = in_dc;
+
+ dc->charset_default = name;
+ return NULL;
+}
+
+/* CharsetOptions optionflag...
+ */
+static const char *add_charset_options(cmd_parms *cmd, void *in_dc,
+ const char *flag)
+{
+ charset_dir_t *dc = in_dc;
+
+ if (!strcasecmp(flag, "ImplicitAdd")) {
+ dc->implicit_add = IA_IMPADD;
+ }
+ else if (!strcasecmp(flag, "NoImplicitAdd")) {
+ dc->implicit_add = IA_NOIMPADD;
+ }
+ else if (!strcasecmp(flag, "TranslateAllMimeTypes")) {
+ dc->force_xlate = FX_FORCE;
+ }
+ else if (!strcasecmp(flag, "NoTranslateAllMimeTypes")) {
+ dc->force_xlate = FX_NOFORCE;
+ }
+ else {
+ return apr_pstrcat(cmd->temp_pool,
+ "Invalid CharsetOptions option: ",
+ flag,
+ NULL);
+ }
+
+ return NULL;
+}
+
+/* find_code_page() is a fixup hook that checks if the module is
+ * configured and the input or output potentially need to be translated.
+ * If so, context is initialized for the filters.
+ */
+static int find_code_page(request_rec *r)
+{
+ charset_dir_t *dc = ap_get_module_config(r->per_dir_config,
+ &charset_lite_module);
+ charset_req_t *reqinfo;
+ charset_filter_ctx_t *input_ctx, *output_ctx;
+ apr_status_t rv;
+
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
+ "uri: %s file: %s method: %d "
+ "imt: %s flags: %s%s%s %s->%s",
+ r->uri,
+ r->filename ? r->filename : "(none)",
+ r->method_number,
+ r->content_type ? r->content_type : "(unknown)",
+ r->main ? "S" : "", /* S if subrequest */
+ r->prev ? "R" : "", /* R if redirect */
+ r->proxyreq ? "P" : "", /* P if proxy */
+ dc->charset_source, dc->charset_default);
+
+ /* If we don't have a full directory configuration, bail out.
+ */
+ if (!dc->charset_source || !dc->charset_default) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01448)
+ "incomplete configuration: src %s, dst %s",
+ dc->charset_source ? dc->charset_source : "unspecified",
+ dc->charset_default ? dc->charset_default : "unspecified");
+ return DECLINED;
+ }
+
+ /* catch proxy requests */
+ if (r->proxyreq) {
+ return DECLINED;
+ }
+
+ /* mod_rewrite indicators */
+ if (r->filename
+ && (!strncmp(r->filename, "redirect:", 9)
+ || !strncmp(r->filename, "gone:", 5)
+ || !strncmp(r->filename, "passthrough:", 12)
+ || !strncmp(r->filename, "forbidden:", 10))) {
+ return DECLINED;
+ }
+
+ /* no translation when server and network charsets are set to the same value */
+ if (!strcasecmp(dc->charset_source, dc->charset_default)) {
+ return DECLINED;
+ }
+
+ /* Get storage for the request data and the output filter context.
+ * We rarely need the input filter context, so allocate that separately.
+ */
+ reqinfo = (charset_req_t *)apr_pcalloc(r->pool,
+ sizeof(charset_req_t) +
+ sizeof(charset_filter_ctx_t));
+ output_ctx = (charset_filter_ctx_t *)(reqinfo + 1);
+
+ reqinfo->dc = dc;
+ output_ctx->dc = dc;
+ output_ctx->tmpbb = apr_brigade_create(r->pool,
+ r->connection->bucket_alloc);
+ ap_set_module_config(r->request_config, &charset_lite_module, reqinfo);
+
+ reqinfo->output_ctx = output_ctx;
+
+ switch (r->method_number) {
+ case M_PUT:
+ case M_POST:
+ /* Set up input translation. Note: A request body can be included
+ * with the OPTIONS method, but for now we don't set up translation
+ * of it.
+ */
+ input_ctx = apr_pcalloc(r->pool, sizeof(charset_filter_ctx_t));
+ input_ctx->bb = apr_brigade_create(r->pool,
+ r->connection->bucket_alloc);
+ input_ctx->tmp = apr_palloc(r->pool, INPUT_XLATE_BUF_SIZE);
+ input_ctx->dc = dc;
+ reqinfo->input_ctx = input_ctx;
+ rv = apr_xlate_open(&input_ctx->xlate, dc->charset_source,
+ dc->charset_default, r->pool);
+ if (rv != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01449)
+ "can't open translation %s->%s",
+ dc->charset_default, dc->charset_source);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ if (apr_xlate_sb_get(input_ctx->xlate, &input_ctx->is_sb) != APR_SUCCESS) {
+ input_ctx->is_sb = 0;
+ }
+ }
+
+ return DECLINED;
+}
+
+static int configured_in_list(request_rec *r, const char *filter_name,
+ struct ap_filter_t *filter_list)
+{
+ struct ap_filter_t *filter = filter_list;
+
+ while (filter) {
+ if (!strcasecmp(filter_name, filter->frec->name)) {
+ return 1;
+ }
+ filter = filter->next;
+ }
+ return 0;
+}
+
+static int configured_on_input(request_rec *r, const char *filter_name)
+{
+ return configured_in_list(r, filter_name, r->input_filters);
+}
+
+static int configured_on_output(request_rec *r, const char *filter_name)
+{
+ return configured_in_list(r, filter_name, r->output_filters);
+}
+
+/* xlate_insert_filter() is a filter hook which decides whether or not
+ * to insert a translation filter for the current request.
+ */
+static void xlate_insert_filter(request_rec *r)
+{
+ /* Hey... don't be so quick to use reqinfo->dc here; reqinfo may be NULL */
+ charset_req_t *reqinfo = ap_get_module_config(r->request_config,
+ &charset_lite_module);
+ charset_dir_t *dc = ap_get_module_config(r->per_dir_config,
+ &charset_lite_module);
+
+ if (dc && (dc->implicit_add == IA_NOIMPADD)) {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE6, 0, r,
+ "xlate output filter not added implicitly because "
+ "CharsetOptions included 'NoImplicitAdd'");
+ return;
+ }
+
+ if (reqinfo) {
+ if (reqinfo->output_ctx && !configured_on_output(r, XLATEOUT_FILTER_NAME)) {
+ ap_add_output_filter(XLATEOUT_FILTER_NAME, reqinfo->output_ctx, r,
+ r->connection);
+ }
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
+ "xlate output filter not added implicitly because %s",
+ !reqinfo->output_ctx ?
+ "no output configuration available" :
+ "another module added the filter");
+
+ if (reqinfo->input_ctx && !configured_on_input(r, XLATEIN_FILTER_NAME)) {
+ ap_add_input_filter(XLATEIN_FILTER_NAME, reqinfo->input_ctx, r,
+ r->connection);
+ }
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
+ "xlate input filter not added implicitly because %s",
+ !reqinfo->input_ctx ?
+ "no input configuration available" :
+ "another module added the filter");
+ }
+}
+
+/* stuff that sucks that I know of:
+ *
+ * bucket handling:
+ * why create an eos bucket when we see it come down the stream? just send the one
+ * passed as input... news flash: this will be fixed when xlate_out_filter() starts
+ * using the more generic xlate_brigade()
+ *
+ * translation mechanics:
+ * we don't handle characters that straddle more than two buckets; an error
+ * will be generated
+ */
+
+static apr_status_t send_bucket_downstream(ap_filter_t *f, apr_bucket *b)
+{
+ charset_filter_ctx_t *ctx = f->ctx;
+ apr_status_t rv;
+
+ APR_BRIGADE_INSERT_TAIL(ctx->tmpbb, b);
+ rv = ap_pass_brigade(f->next, ctx->tmpbb);
+ if (rv != APR_SUCCESS) {
+ ctx->ees = EES_DOWNSTREAM;
+ }
+ apr_brigade_cleanup(ctx->tmpbb);
+ return rv;
+}
+
+/* send_downstream() is passed the translated data; it puts it in a single-
+ * bucket brigade and passes the brigade to the next filter
+ */
+static apr_status_t send_downstream(ap_filter_t *f, const char *tmp, apr_size_t len)
+{
+ request_rec *r = f->r;
+ conn_rec *c = r->connection;
+ apr_bucket *b;
+
+ b = apr_bucket_transient_create(tmp, len, c->bucket_alloc);
+ return send_bucket_downstream(f, b);
+}
+
+static apr_status_t send_eos(ap_filter_t *f)
+{
+ request_rec *r = f->r;
+ conn_rec *c = r->connection;
+ apr_bucket_brigade *bb;
+ apr_bucket *b;
+ charset_filter_ctx_t *ctx = f->ctx;
+ apr_status_t rv;
+
+ bb = apr_brigade_create(r->pool, c->bucket_alloc);
+ b = apr_bucket_eos_create(c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+ rv = ap_pass_brigade(f->next, bb);
+ if (rv != APR_SUCCESS) {
+ ctx->ees = EES_DOWNSTREAM;
+ }
+ return rv;
+}
+
+static apr_status_t set_aside_partial_char(charset_filter_ctx_t *ctx,
+ const char *partial,
+ apr_size_t partial_len)
+{
+ apr_status_t rv;
+
+ if (sizeof(ctx->buf) > partial_len) {
+ ctx->saved = partial_len;
+ memcpy(ctx->buf, partial, partial_len);
+ rv = APR_SUCCESS;
+ }
+ else {
+ rv = APR_INCOMPLETE;
+ ctx->ees = EES_LIMIT; /* we don't handle chars this wide which straddle
+ * buckets
+ */
+ }
+ return rv;
+}
+
+static apr_status_t finish_partial_char(charset_filter_ctx_t *ctx,
+ /* input buffer: */
+ const char **cur_str,
+ apr_size_t *cur_len,
+ /* output buffer: */
+ char **out_str,
+ apr_size_t *out_len)
+{
+ apr_status_t rv;
+ apr_size_t tmp_input_len;
+
+ /* Keep adding bytes from the input string to the saved string until we
+ * 1) finish the input char
+ * 2) get an error
+ * or 3) run out of bytes to add
+ */
+
+ do {
+ ctx->buf[ctx->saved] = **cur_str;
+ ++ctx->saved;
+ ++*cur_str;
+ --*cur_len;
+ tmp_input_len = ctx->saved;
+ rv = apr_xlate_conv_buffer(ctx->xlate,
+ ctx->buf,
+ &tmp_input_len,
+ *out_str,
+ out_len);
+ } while (rv == APR_INCOMPLETE && *cur_len);
+
+ if (rv == APR_SUCCESS) {
+ ctx->saved = 0;
+ }
+ else {
+ ctx->ees = EES_LIMIT; /* code isn't smart enough to handle chars
+ * straddling more than two buckets
+ */
+ }
+
+ return rv;
+}
+
+static void log_xlate_error(ap_filter_t *f, apr_status_t rv)
+{
+ charset_filter_ctx_t *ctx = f->ctx;
+ const char *msg;
+ char msgbuf[100];
+ apr_size_t len;
+
+ switch(ctx->ees) {
+ case EES_LIMIT:
+ rv = 0;
+ msg = APLOGNO(02193) "xlate filter - a built-in restriction was encountered";
+ break;
+ case EES_BAD_INPUT:
+ rv = 0;
+ msg = APLOGNO(02194) "xlate filter - an input character was invalid";
+ break;
+ case EES_BUCKET_READ:
+ rv = 0;
+ msg = APLOGNO(02195) "xlate filter - bucket read routine failed";
+ break;
+ case EES_INCOMPLETE_CHAR:
+ rv = 0;
+ strcpy(msgbuf, APLOGNO(02196) "xlate filter - incomplete char at end of input - ");
+ len = ctx->saved;
+
+ /* We must ensure not to process more than what would fit in the
+ * remaining of the destination buffer, including terminating NULL */
+ if (len > (sizeof(msgbuf) - strlen(msgbuf) - 1) / 2)
+ len = (sizeof(msgbuf) - strlen(msgbuf) - 1) / 2;
+
+ ap_bin2hex(ctx->buf, len, msgbuf + strlen(msgbuf));
+ msg = msgbuf;
+ break;
+ case EES_DOWNSTREAM:
+ msg = APLOGNO(02197) "xlate filter - an error occurred in a lower filter";
+ break;
+ default:
+ msg = APLOGNO(02198) "xlate filter - returning error";
+ }
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, APLOGNO(02997) "%s", msg);
+}
+
+/* chk_filter_chain() is called once per filter instance; it tries to
+ * determine if the current filter instance should be disabled because
+ * its translation is incompatible with the translation of an existing
+ * instance of the translate filter
+ *
+ * Example bad scenario:
+ *
+ * configured filter chain for the request:
+ * INCLUDES XLATEOUT(8859-1->UTS-16)
+ * configured filter chain for the subrequest:
+ * XLATEOUT(8859-1->UTS-16)
+ *
+ * When the subrequest is processed, the filter chain will be
+ * XLATEOUT(8859-1->UTS-16) XLATEOUT(8859-1->UTS-16)
+ * This makes no sense, so the instance of XLATEOUT added for the
+ * subrequest will be noop-ed.
+ *
+ * Example good scenario:
+ *
+ * configured filter chain for the request:
+ * INCLUDES XLATEOUT(8859-1->UTS-16)
+ * configured filter chain for the subrequest:
+ * XLATEOUT(IBM-1047->8859-1)
+ *
+ * When the subrequest is processed, the filter chain will be
+ * XLATEOUT(IBM-1047->8859-1) XLATEOUT(8859-1->UTS-16)
+ * This makes sense, so the instance of XLATEOUT added for the
+ * subrequest will be left alone and it will translate from
+ * IBM-1047->8859-1.
+ */
+static void chk_filter_chain(ap_filter_t *f)
+{
+ ap_filter_t *curf;
+ charset_filter_ctx_t *curctx, *last_xlate_ctx = NULL,
+ *ctx = f->ctx;
+ int output = !strcasecmp(f->frec->name, XLATEOUT_FILTER_NAME);
+
+ if (ctx->noop) {
+ return;
+ }
+
+ /* walk the filter chain; see if it makes sense for our filter to
+ * do any translation
+ */
+ curf = output ? f->r->output_filters : f->r->input_filters;
+ while (curf) {
+ if (!strcasecmp(curf->frec->name, f->frec->name) &&
+ curf->ctx) {
+ curctx = (charset_filter_ctx_t *)curf->ctx;
+ if (!last_xlate_ctx) {
+ last_xlate_ctx = curctx;
+ }
+ else {
+ if (strcmp(last_xlate_ctx->dc->charset_default,
+ curctx->dc->charset_source)) {
+ /* incompatible translation
+ * if our filter instance is incompatible with an instance
+ * already in place, noop our instance
+ * Notes:
+ * . We are only willing to noop our own instance.
+ * . It is possible to noop another instance which has not
+ * yet run, but this is not currently implemented.
+ * Hopefully it will not be needed.
+ * . It is not possible to noop an instance which has
+ * already run.
+ */
+ if (last_xlate_ctx == f->ctx) {
+ last_xlate_ctx->noop = 1;
+ if (APLOGrtrace1(f->r)) {
+ const char *symbol = output ? "->" : "<-";
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG,
+ 0, f->r, APLOGNO(01451)
+ "%s %s - disabling "
+ "translation %s%s%s; existing "
+ "translation %s%s%s",
+ f->r->uri ? "uri" : "file",
+ f->r->uri ? f->r->uri : f->r->filename,
+ last_xlate_ctx->dc->charset_source,
+ symbol,
+ last_xlate_ctx->dc->charset_default,
+ curctx->dc->charset_source,
+ symbol,
+ curctx->dc->charset_default);
+ }
+ }
+ else {
+ const char *symbol = output ? "->" : "<-";
+
+ ap_log_rerror(APLOG_MARK, APLOG_ERR,
+ 0, f->r, APLOGNO(01452)
+ "chk_filter_chain() - can't disable "
+ "translation %s%s%s; existing "
+ "translation %s%s%s",
+ last_xlate_ctx->dc->charset_source,
+ symbol,
+ last_xlate_ctx->dc->charset_default,
+ curctx->dc->charset_source,
+ symbol,
+ curctx->dc->charset_default);
+ }
+ break;
+ }
+ }
+ }
+ curf = curf->next;
+ }
+}
+
+/* xlate_brigade() is used to filter request and response bodies
+ *
+ * we'll stop when one of the following occurs:
+ * . we run out of buckets
+ * . we run out of space in the output buffer
+ * . we hit an error or metadata
+ *
+ * inputs:
+ * bb: brigade to process
+ * buffer: storage to hold the translated characters
+ * buffer_avail: size of buffer
+ * (and a few more uninteresting parms)
+ *
+ * outputs:
+ * return value: APR_SUCCESS or some error code
+ * bb: we've removed any buckets representing the
+ * translated characters; the eos bucket, if
+ * present, will be left in the brigade
+ * buffer: filled in with translated characters
+ * buffer_avail: updated with the bytes remaining
+ * hit_eos: did we hit an EOS bucket?
+ */
+static apr_status_t xlate_brigade(charset_filter_ctx_t *ctx,
+ apr_bucket_brigade *bb,
+ char *buffer,
+ apr_size_t *buffer_avail,
+ int *hit_eos)
+{
+ apr_bucket *b = NULL; /* set to NULL only to quiet some gcc */
+ apr_bucket *consumed_bucket;
+ const char *bucket;
+ apr_size_t bytes_in_bucket; /* total bytes read from current bucket */
+ apr_size_t bucket_avail; /* bytes left in current bucket */
+ apr_status_t rv = APR_SUCCESS;
+
+ *hit_eos = 0;
+ bucket_avail = 0;
+ consumed_bucket = NULL;
+ while (1) {
+ if (!bucket_avail) { /* no bytes left to process in the current bucket... */
+ if (consumed_bucket) {
+ apr_bucket_delete(consumed_bucket);
+ consumed_bucket = NULL;
+ }
+ b = APR_BRIGADE_FIRST(bb);
+ if (b == APR_BRIGADE_SENTINEL(bb) ||
+ APR_BUCKET_IS_METADATA(b)) {
+ break;
+ }
+ rv = apr_bucket_read(b, &bucket, &bytes_in_bucket, APR_BLOCK_READ);
+ if (rv != APR_SUCCESS) {
+ ctx->ees = EES_BUCKET_READ;
+ break;
+ }
+ bucket_avail = bytes_in_bucket;
+ consumed_bucket = b; /* for axing when we're done reading it */
+ }
+ if (bucket_avail) {
+ /* We've got data, so translate it. */
+ if (ctx->saved) {
+ /* Rats... we need to finish a partial character from the previous
+ * bucket.
+ *
+ * Strangely, finish_partial_char() increments the input buffer
+ * pointer but does not increment the output buffer pointer.
+ */
+ apr_size_t old_buffer_avail = *buffer_avail;
+ rv = finish_partial_char(ctx,
+ &bucket, &bucket_avail,
+ &buffer, buffer_avail);
+ buffer += old_buffer_avail - *buffer_avail;
+ }
+ else {
+ apr_size_t old_buffer_avail = *buffer_avail;
+ apr_size_t old_bucket_avail = bucket_avail;
+ rv = apr_xlate_conv_buffer(ctx->xlate,
+ bucket, &bucket_avail,
+ buffer,
+ buffer_avail);
+ buffer += old_buffer_avail - *buffer_avail;
+ bucket += old_bucket_avail - bucket_avail;
+
+ if (rv == APR_INCOMPLETE) { /* partial character at end of input */
+ /* We need to save the final byte(s) for next time; we can't
+ * convert it until we look at the next bucket.
+ */
+ rv = set_aside_partial_char(ctx, bucket, bucket_avail);
+ bucket_avail = 0;
+ }
+ }
+ if (rv != APR_SUCCESS) {
+ /* bad input byte or partial char too big to store */
+ break;
+ }
+ if (*buffer_avail < XLATE_MIN_BUFF_LEFT) {
+ /* if any data remains in the current bucket, split there */
+ if (bucket_avail) {
+ apr_bucket_split(b, bytes_in_bucket - bucket_avail);
+ }
+ apr_bucket_delete(b);
+ break;
+ }
+ }
+ }
+
+ if (!APR_BRIGADE_EMPTY(bb)) {
+ b = APR_BRIGADE_FIRST(bb);
+ if (APR_BUCKET_IS_EOS(b)) {
+ /* Leave the eos bucket in the brigade for reporting to
+ * subsequent filters.
+ */
+ *hit_eos = 1;
+ if (ctx->saved) {
+ /* Oops... we have a partial char from the previous bucket
+ * that won't be completed because there's no more data.
+ */
+ rv = APR_INCOMPLETE;
+ ctx->ees = EES_INCOMPLETE_CHAR;
+ }
+ }
+ }
+
+ return rv;
+}
+
+/* xlate_out_filter() handles (almost) arbitrary conversions from one charset
+ * to another...
+ * translation is determined in the fixup hook (find_code_page), which is
+ * where the filter's context data is set up... the context data gives us
+ * the translation handle
+ */
+static apr_status_t xlate_out_filter(ap_filter_t *f, apr_bucket_brigade *bb)
+{
+ charset_req_t *reqinfo = ap_get_module_config(f->r->request_config,
+ &charset_lite_module);
+ charset_dir_t *dc = ap_get_module_config(f->r->per_dir_config,
+ &charset_lite_module);
+ charset_filter_ctx_t *ctx = f->ctx;
+ apr_bucket *dptr, *consumed_bucket;
+ const char *cur_str;
+ apr_size_t cur_len, cur_avail;
+ char tmp[OUTPUT_XLATE_BUF_SIZE];
+ apr_size_t space_avail;
+ int done;
+ apr_status_t rv = APR_SUCCESS;
+
+ if (!ctx) {
+ /* this is SetOutputFilter path; grab the preallocated context,
+ * if any; note that if we decided not to do anything in an earlier
+ * handler, we won't even have a reqinfo
+ */
+ if (reqinfo) {
+ ctx = f->ctx = reqinfo->output_ctx;
+ reqinfo->output_ctx = NULL; /* prevent SNAFU if user coded us twice
+ * in the filter chain; we can't have two
+ * instances using the same context
+ */
+ }
+ if (!ctx) { /* no idea how to translate; don't do anything */
+ ctx = f->ctx = apr_pcalloc(f->r->pool, sizeof(charset_filter_ctx_t));
+ ctx->dc = dc;
+ ctx->noop = 1;
+ }
+ }
+
+ /* Check the mime type to see if translation should be performed.
+ */
+ if (!ctx->noop && ctx->xlate == NULL) {
+ const char *mime_type = f->r->content_type;
+
+ if (mime_type && (ap_cstr_casecmpn(mime_type, "text/", 5) == 0 ||
+#if APR_CHARSET_EBCDIC
+ /* On an EBCDIC machine, be willing to translate mod_autoindex-
+ * generated output. Otherwise, it doesn't look too cool.
+ *
+ * XXX This isn't a perfect fix because this doesn't trigger us
+ * to convert from the charset of the source code to ASCII. The
+ * general solution seems to be to allow a generator to set an
+ * indicator in the r specifying that the body is coded in the
+ * implementation character set (i.e., the charset of the source
+ * code). This would get several different types of documents
+ * translated properly: mod_autoindex output, mod_status output,
+ * mod_info output, hard-coded error documents, etc.
+ */
+ strcmp(mime_type, DIR_MAGIC_TYPE) == 0 ||
+#endif
+ ap_cstr_casecmpn(mime_type, "message/", 8) == 0 ||
+ dc->force_xlate == FX_FORCE)) {
+
+ rv = apr_xlate_open(&ctx->xlate,
+ dc->charset_default, dc->charset_source, f->r->pool);
+ if (rv != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, APLOGNO(01453)
+ "can't open translation %s->%s",
+ dc->charset_source, dc->charset_default);
+ ctx->noop = 1;
+ }
+ else {
+ if (apr_xlate_sb_get(ctx->xlate, &ctx->is_sb) != APR_SUCCESS) {
+ ctx->is_sb = 0;
+ }
+ }
+ }
+ else {
+ ctx->noop = 1;
+ if (mime_type) {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE6, 0, f->r,
+ "mime type is %s; no translation selected",
+ mime_type);
+ }
+ }
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE6, 0, f->r,
+ "xlate_out_filter() - "
+ "charset_source: %s charset_default: %s",
+ dc && dc->charset_source ? dc->charset_source : "(none)",
+ dc && dc->charset_default ? dc->charset_default : "(none)");
+
+ if (!ctx->ran) { /* filter never ran before */
+ chk_filter_chain(f);
+ ctx->ran = 1;
+ if (!ctx->noop && !ctx->is_sb) {
+ /* We're not converting between two single-byte charsets, so unset
+ * Content-Length since it is unlikely to remain the same.
+ */
+ apr_table_unset(f->r->headers_out, "Content-Length");
+ }
+ }
+
+ if (ctx->noop) {
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ dptr = APR_BRIGADE_FIRST(bb);
+ done = 0;
+ cur_len = 0;
+ space_avail = sizeof(tmp);
+ consumed_bucket = NULL;
+ while (!done) {
+ if (!cur_len) { /* no bytes left to process in the current bucket... */
+ if (consumed_bucket) {
+ apr_bucket_delete(consumed_bucket);
+ consumed_bucket = NULL;
+ }
+ if (dptr == APR_BRIGADE_SENTINEL(bb)) {
+ break;
+ }
+ if (APR_BUCKET_IS_EOS(dptr)) {
+ cur_len = -1; /* XXX yuck, but that tells us to send
+ * eos down; when we minimize our bb construction
+ * we'll fix this crap */
+ if (ctx->saved) {
+ /* Oops... we have a partial char from the previous bucket
+ * that won't be completed because there's no more data.
+ */
+ rv = APR_INCOMPLETE;
+ ctx->ees = EES_INCOMPLETE_CHAR;
+ }
+ break;
+ }
+ if (APR_BUCKET_IS_METADATA(dptr)) {
+ apr_bucket *metadata_bucket;
+ metadata_bucket = dptr;
+ dptr = APR_BUCKET_NEXT(dptr);
+ APR_BUCKET_REMOVE(metadata_bucket);
+ rv = send_bucket_downstream(f, metadata_bucket);
+ if (rv != APR_SUCCESS) {
+ done = 1;
+ }
+ continue;
+ }
+ rv = apr_bucket_read(dptr, &cur_str, &cur_len, APR_BLOCK_READ);
+ if (rv != APR_SUCCESS) {
+ ctx->ees = EES_BUCKET_READ;
+ break;
+ }
+ consumed_bucket = dptr; /* for axing when we're done reading it */
+ dptr = APR_BUCKET_NEXT(dptr); /* get ready for when we access the
+ * next bucket */
+ }
+ /* Try to fill up our tmp buffer with translated data. */
+ cur_avail = cur_len;
+
+ if (cur_len) { /* maybe we just hit the end of a pipe (len = 0) ? */
+ if (ctx->saved) {
+ /* Rats... we need to finish a partial character from the previous
+ * bucket.
+ */
+ char *tmp_tmp;
+
+ tmp_tmp = tmp + sizeof(tmp) - space_avail;
+ rv = finish_partial_char(ctx,
+ &cur_str, &cur_len,
+ &tmp_tmp, &space_avail);
+ }
+ else {
+ rv = apr_xlate_conv_buffer(ctx->xlate,
+ cur_str, &cur_avail,
+ tmp + sizeof(tmp) - space_avail, &space_avail);
+
+ /* Update input ptr and len after consuming some bytes */
+ cur_str += cur_len - cur_avail;
+ cur_len = cur_avail;
+
+ if (rv == APR_INCOMPLETE) { /* partial character at end of input */
+ /* We need to save the final byte(s) for next time; we can't
+ * convert it until we look at the next bucket.
+ */
+ rv = set_aside_partial_char(ctx, cur_str, cur_len);
+ cur_len = 0;
+ }
+ }
+ }
+
+ if (rv != APR_SUCCESS) {
+ /* bad input byte or partial char too big to store */
+ done = 1;
+ }
+
+ if (space_avail < XLATE_MIN_BUFF_LEFT) {
+ /* It is time to flush, as there is not enough space left in the
+ * current output buffer to bother with converting more data.
+ */
+ rv = send_downstream(f, tmp, sizeof(tmp) - space_avail);
+ if (rv != APR_SUCCESS) {
+ done = 1;
+ }
+
+ /* tmp is now empty */
+ space_avail = sizeof(tmp);
+ }
+ }
+
+ if (rv == APR_SUCCESS) {
+ if (space_avail < sizeof(tmp)) { /* gotta write out what we converted */
+ rv = send_downstream(f, tmp, sizeof(tmp) - space_avail);
+ }
+ }
+ if (rv == APR_SUCCESS) {
+ if (cur_len == -1) {
+ rv = send_eos(f);
+ }
+ }
+ else {
+ log_xlate_error(f, rv);
+ }
+
+ return rv;
+}
+
+static apr_status_t xlate_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_status_t rv;
+ charset_req_t *reqinfo = ap_get_module_config(f->r->request_config,
+ &charset_lite_module);
+ charset_dir_t *dc = ap_get_module_config(f->r->per_dir_config,
+ &charset_lite_module);
+ charset_filter_ctx_t *ctx = f->ctx;
+ apr_size_t buffer_size;
+ int hit_eos;
+
+ /* 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);
+ }
+
+ if (!ctx) {
+ /* this is SetInputFilter path; grab the preallocated context,
+ * if any; note that if we decided not to do anything in an earlier
+ * handler, we won't even have a reqinfo
+ */
+ if (reqinfo) {
+ ctx = f->ctx = reqinfo->input_ctx;
+ reqinfo->input_ctx = NULL; /* prevent SNAFU if user coded us twice
+ * in the filter chain; we can't have two
+ * instances using the same context
+ */
+ }
+ if (!ctx) { /* no idea how to translate; don't do anything */
+ ctx = f->ctx = apr_pcalloc(f->r->pool, sizeof(charset_filter_ctx_t));
+ ctx->dc = dc;
+ ctx->noop = 1;
+ }
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE6, 0, f->r,
+ "xlate_in_filter() - "
+ "charset_source: %s charset_default: %s",
+ dc && dc->charset_source ? dc->charset_source : "(none)",
+ dc && dc->charset_default ? dc->charset_default : "(none)");
+
+ if (!ctx->ran) { /* filter never ran before */
+ chk_filter_chain(f);
+ ctx->ran = 1;
+ if (!ctx->noop && !ctx->is_sb
+ && apr_table_get(f->r->headers_in, "Content-Length")) {
+ /* A Content-Length header is present, but it won't be valid after
+ * conversion because we're not converting between two single-byte
+ * charsets. This will affect most CGI scripts and may affect
+ * some modules.
+ * Content-Length can't be unset here because that would break
+ * being able to read the request body.
+ * Processing of chunked request bodies is not impacted by this
+ * filter since the length was not declared anyway.
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, f->r,
+ "Request body length may change, resulting in "
+ "misprocessing by some modules or scripts");
+ }
+ }
+
+ if (ctx->noop) {
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
+
+ if (APR_BRIGADE_EMPTY(ctx->bb)) {
+ if ((rv = ap_get_brigade(f->next, bb, mode, block,
+ readbytes)) != APR_SUCCESS) {
+ return rv;
+ }
+ }
+ else {
+ APR_BRIGADE_PREPEND(bb, ctx->bb); /* first use the leftovers */
+ }
+
+ buffer_size = INPUT_XLATE_BUF_SIZE;
+ rv = xlate_brigade(ctx, bb, ctx->tmp, &buffer_size, &hit_eos);
+ if (rv == APR_SUCCESS) {
+ if (!hit_eos) {
+ /* move anything leftover into our context for next time;
+ * we don't currently "set aside" since the data came from
+ * down below, but I suspect that for long-term we need to
+ * do that
+ */
+ APR_BRIGADE_CONCAT(ctx->bb, bb);
+ }
+ if (buffer_size < INPUT_XLATE_BUF_SIZE) { /* do we have output? */
+ apr_bucket *e;
+
+ e = apr_bucket_heap_create(ctx->tmp,
+ INPUT_XLATE_BUF_SIZE - buffer_size,
+ NULL, f->r->connection->bucket_alloc);
+ /* make sure we insert at the head, because there may be
+ * an eos bucket already there, and the eos bucket should
+ * come after the data
+ */
+ APR_BRIGADE_INSERT_HEAD(bb, e);
+ }
+ else {
+ /* XXX need to get some more data... what if the last brigade
+ * we got had only the first byte of a multibyte char? we need
+ * to grab more data from the network instead of returning an
+ * empty brigade
+ */
+ }
+ /* If we have any metadata at the head of ctx->bb, go ahead and move it
+ * onto the end of bb to be returned to our caller.
+ */
+ if (!APR_BRIGADE_EMPTY(ctx->bb)) {
+ apr_bucket *b = APR_BRIGADE_FIRST(ctx->bb);
+ while (b != APR_BRIGADE_SENTINEL(ctx->bb)
+ && APR_BUCKET_IS_METADATA(b)) {
+ APR_BUCKET_REMOVE(b);
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+ b = APR_BRIGADE_FIRST(ctx->bb);
+ }
+ }
+ }
+ else {
+ log_xlate_error(f, rv);
+ }
+
+ return rv;
+}
+
+static const command_rec cmds[] =
+{
+ AP_INIT_TAKE1("CharsetSourceEnc",
+ add_charset_source,
+ NULL,
+ OR_FILEINFO,
+ "source (html,cgi,ssi) file charset"),
+ AP_INIT_TAKE1("CharsetDefault",
+ add_charset_default,
+ NULL,
+ OR_FILEINFO,
+ "name of default charset"),
+ AP_INIT_ITERATE("CharsetOptions",
+ add_charset_options,
+ NULL,
+ OR_FILEINFO,
+ "valid options: ImplicitAdd, NoImplicitAdd, TranslateAllMimeTypes, "
+ "NoTranslateAllMimeTypes"),
+ {NULL}
+};
+
+static void charset_register_hooks(apr_pool_t *p)
+{
+ ap_hook_fixups(find_code_page, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_insert_filter(xlate_insert_filter, NULL, NULL, APR_HOOK_REALLY_LAST);
+ ap_register_output_filter(XLATEOUT_FILTER_NAME, xlate_out_filter, NULL,
+ AP_FTYPE_RESOURCE);
+ ap_register_input_filter(XLATEIN_FILTER_NAME, xlate_in_filter, NULL,
+ AP_FTYPE_RESOURCE);
+}
+
+AP_DECLARE_MODULE(charset_lite) =
+{
+ STANDARD20_MODULE_STUFF,
+ create_charset_dir_conf,
+ merge_charset_dir_conf,
+ NULL,
+ NULL,
+ cmds,
+ charset_register_hooks
+};
+
diff --git a/modules/filters/mod_charset_lite.dep b/modules/filters/mod_charset_lite.dep
new file mode 100644
index 0000000..3356086
--- /dev/null
+++ b/modules/filters/mod_charset_lite.dep
@@ -0,0 +1,60 @@
+# Microsoft Developer Studio Generated Dependency File, included by mod_charset_lite.mak
+
+..\..\build\win32\httpd.rc : \
+ "..\..\include\ap_release.h"\
+
+
+.\mod_charset_lite.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_main.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_charset.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apr_xlate.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+
diff --git a/modules/filters/mod_charset_lite.dsp b/modules/filters/mod_charset_lite.dsp
new file mode 100644
index 0000000..0fd575f
--- /dev/null
+++ b/modules/filters/mod_charset_lite.dsp
@@ -0,0 +1,111 @@
+# Microsoft Developer Studio Project File - Name="mod_charset_lite" - 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_charset_lite - Win32 Debug
+!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_charset_lite.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_charset_lite.mak" CFG="mod_charset_lite - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_charset_lite - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_charset_lite - 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_charset_lite - 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 /O2 /Oy- /Zi /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_charset_lite_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"Release/mod_charset_lite.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_charset_lite.so" /d LONG_NAME="charset_lite_module for Apache"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_charset_lite.so" /base:@..\..\os\win32\BaseAddr.ref,mod_charset_lite.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_charset_lite.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "mod_charset_lite - 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 "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_charset_lite_src" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"Debug/mod_charset_lite.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_charset_lite.so" /d LONG_NAME="charset_lite_module for Apache"
+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
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_charset_lite.so" /base:@..\..\os\win32\BaseAddr.ref,mod_charset_lite.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_charset_lite.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_charset_lite - Win32 Release"
+# Name "mod_charset_lite - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\mod_charset_lite.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
diff --git a/modules/filters/mod_charset_lite.exp b/modules/filters/mod_charset_lite.exp
new file mode 100644
index 0000000..3f0bf14
--- /dev/null
+++ b/modules/filters/mod_charset_lite.exp
@@ -0,0 +1 @@
+charset_lite_module
diff --git a/modules/filters/mod_charset_lite.mak b/modules/filters/mod_charset_lite.mak
new file mode 100644
index 0000000..b252470
--- /dev/null
+++ b/modules/filters/mod_charset_lite.mak
@@ -0,0 +1,353 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on mod_charset_lite.dsp
+!IF "$(CFG)" == ""
+CFG=mod_charset_lite - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to mod_charset_lite - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "mod_charset_lite - Win32 Release" && "$(CFG)" != "mod_charset_lite - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!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_charset_lite.mak" CFG="mod_charset_lite - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_charset_lite - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_charset_lite - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(CFG)" == "mod_charset_lite - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_charset_lite.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_charset_lite.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_charset_lite.obj"
+ -@erase "$(INTDIR)\mod_charset_lite.res"
+ -@erase "$(INTDIR)\mod_charset_lite_src.idb"
+ -@erase "$(INTDIR)\mod_charset_lite_src.pdb"
+ -@erase "$(OUTDIR)\mod_charset_lite.exp"
+ -@erase "$(OUTDIR)\mod_charset_lite.lib"
+ -@erase "$(OUTDIR)\mod_charset_lite.pdb"
+ -@erase "$(OUTDIR)\mod_charset_lite.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_charset_lite_src" /FD /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_charset_lite.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_charset_lite.so" /d LONG_NAME="charset_lite_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_charset_lite.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_charset_lite.pdb" /debug /out:"$(OUTDIR)\mod_charset_lite.so" /implib:"$(OUTDIR)\mod_charset_lite.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_charset_lite.so /opt:ref
+LINK32_OBJS= \
+ "$(INTDIR)\mod_charset_lite.obj" \
+ "$(INTDIR)\mod_charset_lite.res" \
+ "..\..\srclib\apr\Release\libapr-1.lib" \
+ "..\..\srclib\apr-util\Release\libaprutil-1.lib" \
+ "..\..\Release\libhttpd.lib"
+
+"$(OUTDIR)\mod_charset_lite.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Release\mod_charset_lite.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_charset_lite.so"
+ if exist .\Release\mod_charset_lite.so.manifest mt.exe -manifest .\Release\mod_charset_lite.so.manifest -outputresource:.\Release\mod_charset_lite.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ELSEIF "$(CFG)" == "mod_charset_lite - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_charset_lite.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_charset_lite.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_charset_lite.obj"
+ -@erase "$(INTDIR)\mod_charset_lite.res"
+ -@erase "$(INTDIR)\mod_charset_lite_src.idb"
+ -@erase "$(INTDIR)\mod_charset_lite_src.pdb"
+ -@erase "$(OUTDIR)\mod_charset_lite.exp"
+ -@erase "$(OUTDIR)\mod_charset_lite.lib"
+ -@erase "$(OUTDIR)\mod_charset_lite.pdb"
+ -@erase "$(OUTDIR)\mod_charset_lite.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_charset_lite_src" /FD /EHsc /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_charset_lite.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_charset_lite.so" /d LONG_NAME="charset_lite_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_charset_lite.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_charset_lite.pdb" /debug /out:"$(OUTDIR)\mod_charset_lite.so" /implib:"$(OUTDIR)\mod_charset_lite.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_charset_lite.so
+LINK32_OBJS= \
+ "$(INTDIR)\mod_charset_lite.obj" \
+ "$(INTDIR)\mod_charset_lite.res" \
+ "..\..\srclib\apr\Debug\libapr-1.lib" \
+ "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \
+ "..\..\Debug\libhttpd.lib"
+
+"$(OUTDIR)\mod_charset_lite.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Debug\mod_charset_lite.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_charset_lite.so"
+ if exist .\Debug\mod_charset_lite.so.manifest mt.exe -manifest .\Debug\mod_charset_lite.so.manifest -outputresource:.\Debug\mod_charset_lite.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+
+!IF "$(NO_EXTERNAL_DEPS)" != "1"
+!IF EXISTS("mod_charset_lite.dep")
+!INCLUDE "mod_charset_lite.dep"
+!ELSE
+!MESSAGE Warning: cannot find "mod_charset_lite.dep"
+!ENDIF
+!ENDIF
+
+
+!IF "$(CFG)" == "mod_charset_lite - Win32 Release" || "$(CFG)" == "mod_charset_lite - Win32 Debug"
+
+!IF "$(CFG)" == "mod_charset_lite - Win32 Release"
+
+"libapr - Win32 Release" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_charset_lite - Win32 Debug"
+
+"libapr - Win32 Debug" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_charset_lite - Win32 Release"
+
+"libaprutil - Win32 Release" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_charset_lite - Win32 Debug"
+
+"libaprutil - Win32 Debug" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_charset_lite - Win32 Release"
+
+"libhttpd - Win32 Release" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 ReleaseCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_charset_lite - Win32 Debug"
+
+"libhttpd - Win32 Debug" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 DebugCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ENDIF
+
+SOURCE=..\..\build\win32\httpd.rc
+
+!IF "$(CFG)" == "mod_charset_lite - Win32 Release"
+
+
+"$(INTDIR)\mod_charset_lite.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_charset_lite.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_charset_lite.so" /d LONG_NAME="charset_lite_module for Apache" $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "mod_charset_lite - Win32 Debug"
+
+
+"$(INTDIR)\mod_charset_lite.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_charset_lite.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_charset_lite.so" /d LONG_NAME="charset_lite_module for Apache" $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\mod_charset_lite.c
+
+"$(INTDIR)\mod_charset_lite.obj" : $(SOURCE) "$(INTDIR)"
+
+
+
+!ENDIF
+
diff --git a/modules/filters/mod_data.c b/modules/filters/mod_data.c
new file mode 100644
index 0000000..ddadd1b
--- /dev/null
+++ b/modules/filters/mod_data.c
@@ -0,0 +1,255 @@
+/* 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_data.c --- Turn the response into an rfc2397 data URL, suitable for
+ * displaying as inline content on a page.
+ */
+
+#include "apr.h"
+#include "apr_strings.h"
+#include "apr_buckets.h"
+#include "apr_base64.h"
+#include "apr_lib.h"
+
+#include "ap_config.h"
+#include "util_filter.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_log.h"
+#include "http_request.h"
+#include "http_protocol.h"
+
+#define DATA_FILTER "DATA"
+
+module AP_MODULE_DECLARE_DATA data_module;
+
+typedef struct data_ctx
+{
+ unsigned char overflow[3];
+ int count;
+ apr_bucket_brigade *bb;
+} data_ctx;
+
+/**
+ * Create a data URL as follows:
+ *
+ * data:[<mime-type>;][charset=<charset>;]base64,<payload>
+ *
+ * Where:
+ *
+ * mime-type: The mime type of the original response.
+ * charset: The optional character set corresponding to the mime type.
+ * payload: A base64 version of the response body.
+ *
+ * The content type of the response is set to text/plain.
+ *
+ * The Content-Length header, if present, is updated with the new content
+ * length based on the increase in size expected from the base64 conversion.
+ * If the Content-Length header is too large to fit into an int, we remove
+ * the Content-Length header instead.
+ */
+static apr_status_t data_out_filter(ap_filter_t *f, apr_bucket_brigade *bb)
+{
+ apr_bucket *e, *ee;
+ request_rec *r = f->r;
+ data_ctx *ctx = f->ctx;
+ apr_status_t rv = APR_SUCCESS;
+
+ /* first time in? create a context */
+ if (!ctx) {
+ char *type;
+ char *charset = NULL;
+ char *end;
+ const char *content_length;
+
+ /* base64-ing won't work on subrequests, it would be nice if
+ * it did. Within subrequests, we have no EOS to check for,
+ * so we don't know when to flush the tail to the network.
+ */
+ if (!ap_is_initial_req(f->r)) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
+ ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
+
+ type = apr_pstrdup(r->pool, r->content_type);
+ if (type) {
+ charset = strchr(type, ' ');
+ if (charset) {
+ *charset++ = 0;
+ end = strchr(charset, ' ');
+ if (end) {
+ *end++ = 0;
+ }
+ }
+ }
+
+ apr_brigade_printf(ctx->bb, NULL, NULL, "data:%s%s;base64,",
+ type ? type : "", charset ? charset : "");
+
+ content_length = apr_table_get(r->headers_out, "Content-Length");
+ if (content_length) {
+ apr_off_t len, clen;
+ apr_brigade_length(ctx->bb, 1, &len);
+ if (ap_parse_strict_length(&clen, content_length)
+ && clen < APR_INT32_MAX) {
+ ap_set_content_length(r, len +
+ apr_base64_encode_len((int)clen) - 1);
+ }
+ else {
+ apr_table_unset(r->headers_out, "Content-Length");
+ }
+ }
+
+ ap_set_content_type(r, "text/plain");
+
+ }
+
+ /* Do nothing if asked to filter nothing. */
+ if (APR_BRIGADE_EMPTY(bb)) {
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ while (APR_SUCCESS == rv && !APR_BRIGADE_EMPTY(bb)) {
+ const char *data;
+ apr_size_t size;
+ apr_size_t tail;
+ apr_size_t len;
+ /* buffer big enough for 8000 encoded bytes (6000 raw bytes) and terminator */
+ char buffer[APR_BUCKET_BUFF_SIZE + 1];
+ char encoded[((sizeof(ctx->overflow)) / 3) * 4 + 1];
+
+ e = APR_BRIGADE_FIRST(bb);
+
+ /* EOS means we are done. */
+ if (APR_BUCKET_IS_EOS(e)) {
+
+ /* write away the tail */
+ if (ctx->count) {
+ len = apr_base64_encode_binary(encoded, ctx->overflow,
+ ctx->count);
+ apr_brigade_write(ctx->bb, NULL, NULL, encoded, len - 1);
+ ctx->count = 0;
+ }
+
+ /* pass the EOS across */
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+
+ /* pass what we have down the chain */
+ ap_remove_output_filter(f);
+ rv = ap_pass_brigade(f->next, ctx->bb);
+
+ /* pass any stray buckets after the EOS down the stack */
+ if ((APR_SUCCESS == rv) && (!APR_BRIGADE_EMPTY(bb))) {
+ rv = ap_pass_brigade(f->next, bb);
+ }
+ continue;
+ }
+
+ /* flush what we can, we can't flush the tail until EOS */
+ if (APR_BUCKET_IS_FLUSH(e)) {
+
+ /* pass the flush bucket across */
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+
+ /* pass what we have down the chain */
+ rv = ap_pass_brigade(f->next, ctx->bb);
+ continue;
+ }
+
+ /* metadata buckets are preserved as is */
+ if (APR_BUCKET_IS_METADATA(e)) {
+ /*
+ * Remove meta data bucket from old brigade and insert into the
+ * new.
+ */
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+ continue;
+ }
+
+ /* make sure we don't read more than 6000 bytes at a time */
+ apr_brigade_partition(bb, (APR_BUCKET_BUFF_SIZE / 4 * 3), &ee);
+
+ /* size will never be more than 6000 bytes */
+ if (APR_SUCCESS == (rv = apr_bucket_read(e, &data, &size,
+ APR_BLOCK_READ))) {
+
+ /* fill up and write out our overflow buffer if partially used */
+ while (size && ctx->count && ctx->count < sizeof(ctx->overflow)) {
+ ctx->overflow[ctx->count++] = *data++;
+ size--;
+ }
+ if (ctx->count == sizeof(ctx->overflow)) {
+ len = apr_base64_encode_binary(encoded, ctx->overflow,
+ sizeof(ctx->overflow));
+ apr_brigade_write(ctx->bb, NULL, NULL, encoded, len - 1);
+ ctx->count = 0;
+ }
+
+ /* write the main base64 chunk */
+ tail = size % sizeof(ctx->overflow);
+ size -= tail;
+ if (size) {
+ len = apr_base64_encode_binary(buffer,
+ (const unsigned char *) data, size);
+ apr_brigade_write(ctx->bb, NULL, NULL, buffer, len - 1);
+ }
+
+ /* save away any tail in the overflow buffer */
+ if (tail) {
+ memcpy(ctx->overflow, data + size, tail);
+ ctx->count += tail;
+ }
+
+ apr_bucket_delete(e);
+
+ /* pass what we have down the chain */
+ rv = ap_pass_brigade(f->next, ctx->bb);
+ if (rv) {
+ /* should break out of the loop, since our write to the client
+ * failed in some way. */
+ continue;
+ }
+
+ }
+
+ }
+
+ return rv;
+
+}
+
+static const command_rec data_cmds[] = { { NULL } };
+
+static void register_hooks(apr_pool_t *p)
+{
+ ap_register_output_filter(DATA_FILTER, data_out_filter, NULL,
+ AP_FTYPE_RESOURCE);
+}
+AP_DECLARE_MODULE(data) = { STANDARD20_MODULE_STUFF,
+ NULL, /* create per-directory config structure */
+ NULL, /* merge per-directory config structures */
+ NULL, /* create per-server config structure */
+ NULL, /* merge per-server config structures */
+ data_cmds, /* command apr_table_t */
+ register_hooks /* register hooks */
+};
diff --git a/modules/filters/mod_data.dep b/modules/filters/mod_data.dep
new file mode 100644
index 0000000..dfcc43d
--- /dev/null
+++ b/modules/filters/mod_data.dep
@@ -0,0 +1,55 @@
+# Microsoft Developer Studio Generated Dependency File, included by mod_data.mak
+
+..\..\build\win32\httpd.rc : \
+ "..\..\include\ap_release.h"\
+
+
+.\mod_data.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\srclib\apr-util\include\apr_base64.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+
diff --git a/modules/filters/mod_data.dsp b/modules/filters/mod_data.dsp
new file mode 100644
index 0000000..e4cc23c
--- /dev/null
+++ b/modules/filters/mod_data.dsp
@@ -0,0 +1,111 @@
+# Microsoft Developer Studio Project File - Name="mod_data" - 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_data - 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_data.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_data.mak" CFG="mod_data - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_data - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_data - 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_data - 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" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_data_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 /fo"Release/mod_data.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_data.so" /d LONG_NAME="data_module for Apache"
+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_data.so" /base:@..\..\os\win32\BaseAddr.ref,mod_data.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_data.so" /base:@..\..\os\win32\BaseAddr.ref,mod_data.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_data.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "mod_data - 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" /D "HAVE_ZUTIL_H" /Fd"Debug\mod_data_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 /fo"Debug/mod_data.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_data.so" /d LONG_NAME="data_module for Apache"
+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_data.so" /base:@..\..\os\win32\BaseAddr.ref,mod_data.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_data.so" /base:@..\..\os\win32\BaseAddr.ref,mod_data.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_data.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_data - Win32 Release"
+# Name "mod_data - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\mod_data.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
diff --git a/modules/filters/mod_data.mak b/modules/filters/mod_data.mak
new file mode 100644
index 0000000..cc73f51
--- /dev/null
+++ b/modules/filters/mod_data.mak
@@ -0,0 +1,353 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on mod_data.dsp
+!IF "$(CFG)" == ""
+CFG=mod_data - Win32 Release
+!MESSAGE No configuration specified. Defaulting to mod_data - Win32 Release.
+!ENDIF
+
+!IF "$(CFG)" != "mod_data - Win32 Release" && "$(CFG)" != "mod_data - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!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_data.mak" CFG="mod_data - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_data - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_data - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(CFG)" == "mod_data - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_data.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_data.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_data.obj"
+ -@erase "$(INTDIR)\mod_data.res"
+ -@erase "$(INTDIR)\mod_data_src.idb"
+ -@erase "$(INTDIR)\mod_data_src.pdb"
+ -@erase "$(OUTDIR)\mod_data.exp"
+ -@erase "$(OUTDIR)\mod_data.lib"
+ -@erase "$(OUTDIR)\mod_data.pdb"
+ -@erase "$(OUTDIR)\mod_data.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_data_src" /FD /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_data.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_data.so" /d LONG_NAME="data_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_data.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_data.pdb" /debug /out:"$(OUTDIR)\mod_data.so" /implib:"$(OUTDIR)\mod_data.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_data.so /opt:ref
+LINK32_OBJS= \
+ "$(INTDIR)\mod_data.obj" \
+ "$(INTDIR)\mod_data.res" \
+ "..\..\srclib\apr\Release\libapr-1.lib" \
+ "..\..\srclib\apr-util\Release\libaprutil-1.lib" \
+ "..\..\Release\libhttpd.lib"
+
+"$(OUTDIR)\mod_data.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Release\mod_data.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_data.so"
+ if exist .\Release\mod_data.so.manifest mt.exe -manifest .\Release\mod_data.so.manifest -outputresource:.\Release\mod_data.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ELSEIF "$(CFG)" == "mod_data - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_data.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_data.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_data.obj"
+ -@erase "$(INTDIR)\mod_data.res"
+ -@erase "$(INTDIR)\mod_data_src.idb"
+ -@erase "$(INTDIR)\mod_data_src.pdb"
+ -@erase "$(OUTDIR)\mod_data.exp"
+ -@erase "$(OUTDIR)\mod_data.lib"
+ -@erase "$(OUTDIR)\mod_data.pdb"
+ -@erase "$(OUTDIR)\mod_data.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "HAVE_ZUTIL_H" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_data_src" /FD /EHsc /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_data.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_data.so" /d LONG_NAME="data_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_data.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_data.pdb" /debug /out:"$(OUTDIR)\mod_data.so" /implib:"$(OUTDIR)\mod_data.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_data.so
+LINK32_OBJS= \
+ "$(INTDIR)\mod_data.obj" \
+ "$(INTDIR)\mod_data.res" \
+ "..\..\srclib\apr\Debug\libapr-1.lib" \
+ "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \
+ "..\..\Debug\libhttpd.lib"
+
+"$(OUTDIR)\mod_data.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Debug\mod_data.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_data.so"
+ if exist .\Debug\mod_data.so.manifest mt.exe -manifest .\Debug\mod_data.so.manifest -outputresource:.\Debug\mod_data.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+
+!IF "$(NO_EXTERNAL_DEPS)" != "1"
+!IF EXISTS("mod_data.dep")
+!INCLUDE "mod_data.dep"
+!ELSE
+!MESSAGE Warning: cannot find "mod_data.dep"
+!ENDIF
+!ENDIF
+
+
+!IF "$(CFG)" == "mod_data - Win32 Release" || "$(CFG)" == "mod_data - Win32 Debug"
+
+!IF "$(CFG)" == "mod_data - Win32 Release"
+
+"libapr - Win32 Release" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_data - Win32 Debug"
+
+"libapr - Win32 Debug" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_data - Win32 Release"
+
+"libaprutil - Win32 Release" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_data - Win32 Debug"
+
+"libaprutil - Win32 Debug" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_data - Win32 Release"
+
+"libhttpd - Win32 Release" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 ReleaseCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_data - Win32 Debug"
+
+"libhttpd - Win32 Debug" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 DebugCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ENDIF
+
+SOURCE=..\..\build\win32\httpd.rc
+
+!IF "$(CFG)" == "mod_data - Win32 Release"
+
+
+"$(INTDIR)\mod_data.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_data.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_data.so" /d LONG_NAME="data_module for Apache" $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "mod_data - Win32 Debug"
+
+
+"$(INTDIR)\mod_data.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_data.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_data.so" /d LONG_NAME="data_module for Apache" $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\mod_data.c
+
+"$(INTDIR)\mod_data.obj" : $(SOURCE) "$(INTDIR)"
+
+
+
+!ENDIF
+
diff --git a/modules/filters/mod_deflate.c b/modules/filters/mod_deflate.c
new file mode 100644
index 0000000..2431fd7
--- /dev/null
+++ b/modules/filters/mod_deflate.c
@@ -0,0 +1,1936 @@
+/* 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_deflate.c: Perform deflate content-encoding on the fly
+ *
+ * Written by Ian Holsman, Justin Erenkrantz, and Nick Kew
+ */
+
+/*
+ * Portions of this software are based upon zlib code by Jean-loup Gailly
+ * (zlib functions gz_open and gzwrite, check_header)
+ */
+
+/* zlib flags */
+#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
+#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
+#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
+#define COMMENT 0x10 /* bit 4 set: file comment present */
+#define RESERVED 0xE0 /* bits 5..7: reserved */
+
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_log.h"
+#include "http_core.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include "apr_general.h"
+#include "util_filter.h"
+#include "apr_buckets.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "http_ssl.h"
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+
+#include "zlib.h"
+
+static const char deflateFilterName[] = "DEFLATE";
+module AP_MODULE_DECLARE_DATA deflate_module;
+
+#define AP_INFLATE_RATIO_LIMIT 200
+#define AP_INFLATE_RATIO_BURST 3
+
+typedef struct deflate_filter_config_t
+{
+ int windowSize;
+ int memlevel;
+ int compressionlevel;
+ apr_size_t bufferSize;
+ const char *note_ratio_name;
+ const char *note_input_name;
+ const char *note_output_name;
+} deflate_filter_config;
+
+typedef struct deflate_dirconf_t {
+ apr_off_t inflate_limit;
+ int ratio_limit,
+ ratio_burst;
+} deflate_dirconf_t;
+
+/* RFC 1952 Section 2.3 defines the gzip header:
+ *
+ * +---+---+---+---+---+---+---+---+---+---+
+ * |ID1|ID2|CM |FLG| MTIME |XFL|OS |
+ * +---+---+---+---+---+---+---+---+---+---+
+ */
+static const char gzip_header[10] =
+{ '\037', '\213', Z_DEFLATED, 0,
+ 0, 0, 0, 0, /* mtime */
+ 0, 0x03 /* Unix OS_CODE */
+};
+
+/* magic header */
+static const char deflate_magic[2] = { '\037', '\213' };
+
+/* 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
+
+/* Check whether a request is gzipped, so we can un-gzip it.
+ * If a request has multiple encodings, we need the gzip
+ * to be the outermost non-identity encoding.
+ */
+static int check_gzip(request_rec *r, apr_table_t *hdrs1, apr_table_t *hdrs2)
+{
+ int found = 0;
+ apr_table_t *hdrs = hdrs1;
+ const char *encoding = apr_table_get(hdrs, "Content-Encoding");
+
+ if (!encoding && (hdrs2 != NULL)) {
+ /* the output filter has two tables and a content_encoding to check */
+ encoding = apr_table_get(hdrs2, "Content-Encoding");
+ hdrs = hdrs2;
+ if (!encoding) {
+ encoding = r->content_encoding;
+ hdrs = NULL;
+ }
+ }
+ if (encoding && *encoding) {
+
+ /* check the usual/simple case first */
+ if (!ap_cstr_casecmp(encoding, "gzip")
+ || !ap_cstr_casecmp(encoding, "x-gzip")) {
+ found = 1;
+ if (hdrs) {
+ apr_table_unset(hdrs, "Content-Encoding");
+ }
+ else {
+ r->content_encoding = NULL;
+ }
+ }
+ else if (ap_strchr_c(encoding, ',') != NULL) {
+ /* If the outermost encoding isn't gzip, there's nothing
+ * we can do. So only check the last non-identity token
+ */
+ char *new_encoding = apr_pstrdup(r->pool, encoding);
+ char *ptr;
+ for(;;) {
+ char *token = ap_strrchr(new_encoding, ',');
+ if (!token) { /* gzip:identity or other:identity */
+ if (!ap_cstr_casecmp(new_encoding, "gzip")
+ || !ap_cstr_casecmp(new_encoding, "x-gzip")) {
+ found = 1;
+ if (hdrs) {
+ apr_table_unset(hdrs, "Content-Encoding");
+ }
+ else {
+ r->content_encoding = NULL;
+ }
+ }
+ break; /* seen all tokens */
+ }
+ for (ptr=token+1; apr_isspace(*ptr); ++ptr);
+ if (!ap_cstr_casecmp(ptr, "gzip")
+ || !ap_cstr_casecmp(ptr, "x-gzip")) {
+ *token = '\0';
+ if (hdrs) {
+ apr_table_setn(hdrs, "Content-Encoding", new_encoding);
+ }
+ else {
+ r->content_encoding = new_encoding;
+ }
+ found = 1;
+ }
+ else if (!ptr[0] || !ap_cstr_casecmp(ptr, "identity")) {
+ *token = '\0';
+ continue; /* strip the token and find the next one */
+ }
+ break; /* found a non-identity token */
+ }
+ }
+ }
+ /*
+ * If we have dealt with the headers above but content_encoding was set
+ * before sync it with the new value in the hdrs table as
+ * r->content_encoding takes precedence later on in the http_header_filter
+ * and hence would destroy what we have just set in the hdrs table.
+ */
+ if (hdrs && r->content_encoding) {
+ r->content_encoding = apr_table_get(hdrs, "Content-Encoding");
+ }
+ return found;
+}
+
+/* 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 void *create_deflate_dirconf(apr_pool_t *p, char *dummy)
+{
+ deflate_dirconf_t *dc = apr_pcalloc(p, sizeof(*dc));
+ dc->ratio_limit = AP_INFLATE_RATIO_LIMIT;
+ dc->ratio_burst = AP_INFLATE_RATIO_BURST;
+ return dc;
+}
+
+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 = arg1;
+ }
+ else if (!strcasecmp(arg1, "ratio")) {
+ c->note_ratio_name = arg2;
+ }
+ else if (!strcasecmp(arg1, "input")) {
+ c->note_input_name = arg2;
+ }
+ else if (!strcasecmp(arg1, "output")) {
+ c->note_output_name = 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;
+}
+
+
+static const char *deflate_set_inflate_limit(cmd_parms *cmd, void *dirconf,
+ const char *arg)
+{
+ deflate_dirconf_t *dc = (deflate_dirconf_t*) dirconf;
+ char *errp;
+
+ if (APR_SUCCESS != apr_strtoff(&dc->inflate_limit, arg, &errp, 10)) {
+ return "DeflateInflateLimitRequestBody is not parsable.";
+ }
+ if (*errp || dc->inflate_limit < 0) {
+ return "DeflateInflateLimitRequestBody requires a non-negative integer.";
+ }
+
+ return NULL;
+}
+
+static const char *deflate_set_inflate_ratio_limit(cmd_parms *cmd,
+ void *dirconf,
+ const char *arg)
+{
+ deflate_dirconf_t *dc = (deflate_dirconf_t*) dirconf;
+ int i;
+
+ i = atoi(arg);
+ if (i <= 0)
+ return "DeflateInflateRatioLimit must be positive";
+
+ dc->ratio_limit = i;
+
+ return NULL;
+}
+
+static const char *deflate_set_inflate_ratio_burst(cmd_parms *cmd,
+ void *dirconf,
+ const char *arg)
+{
+ deflate_dirconf_t *dc = (deflate_dirconf_t*) dirconf;
+ int i;
+
+ i = atoi(arg);
+ if (i <= 0)
+ return "DeflateInflateRatioBurst must be positive";
+
+ dc->ratio_burst = i;
+
+ return NULL;
+}
+
+typedef struct deflate_ctx_t
+{
+ z_stream stream;
+ unsigned char *buffer;
+ unsigned long crc;
+ apr_bucket_brigade *bb, *proc_bb;
+ int (*libz_end_func)(z_streamp);
+ unsigned char *validation_buffer;
+ apr_size_t validation_buffer_length;
+ char header[10]; /* sizeof(gzip_header) */
+ apr_size_t header_len;
+ int zlib_flags;
+ int ratio_hits;
+ apr_off_t inflate_total;
+ unsigned int consume_pos,
+ consume_len;
+ unsigned int filter_init:1;
+ unsigned int done:1;
+} deflate_ctx;
+
+/* Number of validation bytes (CRC and length) after the compressed data */
+#define VALIDATION_SIZE 8
+/* Do not update ctx->crc, see comment in flush_libz_buffer */
+#define NO_UPDATE_CRC 0
+/* Do update ctx->crc, see comment in flush_libz_buffer */
+#define UPDATE_CRC 1
+
+static int flush_libz_buffer(deflate_ctx *ctx, deflate_filter_config *c,
+ struct apr_bucket_alloc_t *bucket_alloc,
+ int (*libz_func)(z_streamp, int), int flush,
+ int crc)
+{
+ int zRC = Z_OK;
+ int done = 0;
+ unsigned int deflate_len;
+ apr_bucket *b;
+
+ for (;;) {
+ deflate_len = c->bufferSize - ctx->stream.avail_out;
+
+ if (deflate_len != 0) {
+ /*
+ * Do we need to update ctx->crc? Usually this is the case for
+ * inflate action where we need to do a crc on the output, whereas
+ * in the deflate case we need to do a crc on the input
+ */
+ if (crc) {
+ ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer,
+ deflate_len);
+ }
+ b = apr_bucket_heap_create((char *)ctx->buffer,
+ deflate_len, NULL,
+ 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 = libz_func(&ctx->stream, flush);
+
+ /*
+ * We can ignore Z_BUF_ERROR because:
+ * When we call libz_func we can assume that
+ *
+ * - avail_in is zero (due to the surrounding code that calls
+ * flush_libz_buffer)
+ * - avail_out is non zero due to our actions some lines above
+ *
+ * So the only reason for Z_BUF_ERROR is that the internal libz
+ * buffers are now empty and thus we called libz_func one time
+ * too often. This does not hurt. It simply says that we are done.
+ */
+ if (zRC == Z_BUF_ERROR) {
+ zRC = Z_OK;
+ break;
+ }
+
+ done = (ctx->stream.avail_out != 0 || zRC == Z_STREAM_END);
+
+ if (zRC != Z_OK && zRC != Z_STREAM_END)
+ break;
+ }
+ return zRC;
+}
+
+static apr_status_t deflate_ctx_cleanup(void *data)
+{
+ deflate_ctx *ctx = (deflate_ctx *)data;
+
+ if (ctx)
+ ctx->libz_end_func(&ctx->stream);
+ return APR_SUCCESS;
+}
+
+/* ETag must be unique among the possible representations, so a change
+ * to content-encoding requires a corresponding change to the ETag.
+ * This routine appends -transform (e.g., -gzip) to the entity-tag
+ * value inside the double-quotes if an ETag has already been set
+ * and its value already contains double-quotes. PR 39727
+ */
+static void deflate_check_etag(request_rec *r, const char *transform)
+{
+ const char *etag = apr_table_get(r->headers_out, "ETag");
+ apr_size_t etaglen;
+
+ if ((etag && ((etaglen = strlen(etag)) > 2))) {
+ if (etag[etaglen - 1] == '"') {
+ apr_size_t transformlen = strlen(transform);
+ char *newtag = apr_palloc(r->pool, etaglen + transformlen + 2);
+ char *d = newtag;
+ char *e = d + etaglen - 1;
+ const char *s = etag;
+
+ for (; d < e; ++d, ++s) {
+ *d = *s; /* copy etag to newtag up to last quote */
+ }
+ *d++ = '-'; /* append dash to newtag */
+ s = transform;
+ e = d + transformlen;
+ for (; d < e; ++d, ++s) {
+ *d = *s; /* copy transform to newtag */
+ }
+ *d++ = '"'; /* append quote to newtag */
+ *d = '\0'; /* null terminate newtag */
+
+ apr_table_setn(r->headers_out, "ETag", newtag);
+ }
+ }
+}
+
+/* Check whether the (inflate) ratio exceeds the configured limit/burst. */
+static int check_ratio(request_rec *r, deflate_ctx *ctx,
+ const deflate_dirconf_t *dc)
+{
+ if (ctx->stream.total_in) {
+ int ratio = ctx->stream.total_out / ctx->stream.total_in;
+ if (ratio < dc->ratio_limit) {
+ ctx->ratio_hits = 0;
+ }
+ else if (++ctx->ratio_hits > dc->ratio_burst) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int have_ssl_compression(request_rec *r)
+{
+ const char *comp;
+ comp = ap_ssl_var_lookup(r->pool, r->server, r->connection, r,
+ "SSL_COMPRESS_METHOD");
+ if (comp == NULL || *comp == '\0' || strcmp(comp, "NULL") == 0)
+ return 0;
+ return 1;
+}
+
+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;
+ apr_size_t len = 0, blen;
+ const char *data;
+ deflate_filter_config *c;
+
+ /* Do nothing if asked to filter nothing. */
+ if (APR_BRIGADE_EMPTY(bb)) {
+ return APR_SUCCESS;
+ }
+
+ 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 *token;
+ const char *encoding;
+
+ if (have_ssl_compression(r)) {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+ "Compression enabled at SSL level; not compressing "
+ "at HTTP level.");
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ /* We have checked above that bb is not empty */
+ e = APR_BRIGADE_LAST(bb);
+ if (APR_BUCKET_IS_EOS(e)) {
+ /*
+ * If we already know the size of the response, we can skip
+ * compression on responses smaller than the compression overhead.
+ * However, if we compress, we must initialize deflate_out before
+ * calling ap_pass_brigade() for the first time. Otherwise the
+ * headers will be sent to the client without
+ * "Content-Encoding: gzip".
+ */
+ e = APR_BRIGADE_FIRST(bb);
+ while (1) {
+ apr_status_t rc;
+ if (APR_BUCKET_IS_EOS(e)) {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+ "Not compressing very small response of %"
+ APR_SIZE_T_FMT " bytes", len);
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+ if (APR_BUCKET_IS_METADATA(e)) {
+ e = APR_BUCKET_NEXT(e);
+ continue;
+ }
+
+ if (e->length == (apr_size_t)-1) {
+ rc = apr_bucket_read(e, &data, &blen, APR_BLOCK_READ);
+ if (rc != APR_SUCCESS)
+ return rc;
+ }
+ else {
+ blen = e->length;
+ }
+ len += blen;
+ /* 50 is for Content-Encoding and Vary headers and ETag suffix */
+ if (len > sizeof(gzip_header) + VALIDATION_SIZE + 50)
+ break;
+
+ e = APR_BUCKET_NEXT(e);
+ }
+ }
+
+ ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
+
+ /*
+ * Only work on main request, not subrequests,
+ * that are not a 204 response with no content
+ * and are not tagged with the no-gzip env variable
+ * and not a partial response to a Range request.
+ *
+ * Note that responding to 304 is handled separately to
+ * set the required headers (such as ETag) per RFC7232, 4.1.
+ */
+ if ((r->main != NULL) || (r->status == HTTP_NO_CONTENT) ||
+ apr_table_get(r->subprocess_env, "no-gzip") ||
+ apr_table_get(r->headers_out, "Content-Range")
+ ) {
+ if (APLOG_R_IS_LEVEL(r, APLOG_TRACE1)) {
+ const char *reason =
+ (r->main != NULL) ? "subrequest" :
+ (r->status == HTTP_NO_CONTENT) ? "no content" :
+ apr_table_get(r->subprocess_env, "no-gzip") ? "no-gzip" :
+ "content-range";
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+ "Not compressing (%s)", reason);
+ }
+ 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_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+ "Not compressing, (gzip-only-text/html)");
+ 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_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+ "Not compressing (content-encoding already "
+ " set: %s)", token);
+ 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");
+
+ /* force-gzip will just force it out regardless if the browser
+ * can actually do anything with it.
+ */
+ if (!apr_table_get(r->subprocess_env, "force-gzip")) {
+ const char *accepts;
+ const char *q = NULL;
+
+ /* 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] && ap_cstr_casecmp(token, "gzip")) {
+ /* skip parameters, XXX: ;q=foo evaluation? */
+ while (*accepts == ';') {
+ ++accepts;
+ ap_get_token(r->pool, &accepts, 1);
+ }
+
+ /* retrieve next token */
+ if (*accepts == ',') {
+ ++accepts;
+ }
+ token = (*accepts) ? ap_get_token(r->pool, &accepts, 0) : NULL;
+ }
+
+ /* Find the qvalue, if provided */
+ if (*accepts) {
+ while (*accepts == ';') {
+ ++accepts;
+ }
+ q = ap_get_token(r->pool, &accepts, 1);
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+ "token: '%s' - q: '%s'", token ? token : "NULL", q);
+ }
+
+ /* No acceptable token found or q=0 */
+ if (!token || token[0] == '\0' ||
+ (q && strlen(q) >= 3 && strncmp("q=0.000", q, strlen(q)) == 0)) {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+ "Not compressing (no Accept-Encoding: gzip or q=0)");
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+ "Forcing compression (force-gzip set)");
+ }
+
+ /* At this point we have decided to filter the content. Let's try to
+ * to initialize zlib (except for 304 responses, where we will only
+ * send out the headers).
+ */
+
+ if (r->status != HTTP_NOT_MODIFIED) {
+ ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
+ ctx->buffer = apr_palloc(r->pool, c->bufferSize);
+ ctx->libz_end_func = deflateEnd;
+
+ zRC = deflateInit2(&ctx->stream, c->compressionlevel, Z_DEFLATED,
+ c->windowSize, c->memlevel,
+ Z_DEFAULT_STRATEGY);
+
+ if (zRC != Z_OK) {
+ deflateEnd(&ctx->stream);
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01383)
+ "unable to init Zlib: "
+ "deflateInit2 returned %d: URL %s",
+ zRC, r->uri);
+ /*
+ * Remove ourselves as it does not make sense to return:
+ * We are not able to init libz and pass data down the chain
+ * uncompressed.
+ */
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+ /*
+ * Register a cleanup function to ensure that we cleanup the internal
+ * libz resources.
+ */
+ apr_pool_cleanup_register(r->pool, ctx, deflate_ctx_cleanup,
+ apr_pool_cleanup_null);
+
+ /* Set the filter init flag so subsequent invocations know we are
+ * active.
+ */
+ ctx->filter_init = 1;
+ }
+
+ /*
+ * Zlib initialization worked, so we can now change the important
+ * content metadata before sending the response out.
+ */
+
+ /* If the entire Content-Encoding is "identity", we can replace it. */
+ if (!encoding || !ap_cstr_casecmp(encoding, "identity")) {
+ apr_table_setn(r->headers_out, "Content-Encoding", "gzip");
+ }
+ else {
+ apr_table_mergen(r->headers_out, "Content-Encoding", "gzip");
+ }
+ /* Fix r->content_encoding if it was set before */
+ if (r->content_encoding) {
+ r->content_encoding = apr_table_get(r->headers_out,
+ "Content-Encoding");
+ }
+ apr_table_unset(r->headers_out, "Content-Length");
+ apr_table_unset(r->headers_out, "Content-MD5");
+ deflate_check_etag(r, "gzip");
+
+ /* For a 304 response, only change the headers */
+ if (r->status == HTTP_NOT_MODIFIED) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ /* add immortal gzip header */
+ e = apr_bucket_immortal_create(gzip_header, sizeof gzip_header,
+ f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+
+ /* initialize deflate output buffer */
+ ctx->stream.next_out = ctx->buffer;
+ ctx->stream.avail_out = c->bufferSize;
+ } else if (!ctx->filter_init) {
+ /* Hmm. We've run through the filter init before as we have a ctx,
+ * but we never initialized. We probably have a dangling ref. Bail.
+ */
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ while (!APR_BRIGADE_EMPTY(bb))
+ {
+ apr_bucket *b;
+
+ /*
+ * Optimization: If we are a HEAD request and bytes_sent is not zero
+ * it means that we have passed the content-length filter once and
+ * have more data to sent. This means that the content-length filter
+ * could not determine our content-length for the response to the
+ * HEAD request anyway (the associated GET request would deliver the
+ * body in chunked encoding) and we can stop compressing.
+ */
+ if (r->header_only && r->bytes_sent) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ e = APR_BRIGADE_FIRST(bb);
+
+ if (APR_BUCKET_IS_EOS(e)) {
+ char *buf;
+
+ ctx->stream.avail_in = 0; /* should be zero already anyway */
+ /* flush the remaining data from the zlib buffers */
+ flush_libz_buffer(ctx, c, f->c->bucket_alloc, deflate, Z_FINISH,
+ NO_UPDATE_CRC);
+
+ buf = apr_palloc(r->pool, VALIDATION_SIZE);
+ putLong((unsigned char *)&buf[0], ctx->crc);
+ putLong((unsigned char *)&buf[4], ctx->stream.total_in);
+
+ b = apr_bucket_pool_create(buf, VALIDATION_SIZE, r->pool,
+ f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01384)
+ "Zlib: Compressed %" APR_UINT64_T_FMT
+ " to %" APR_UINT64_T_FMT " : URL %s",
+ (apr_uint64_t)ctx->stream.total_in,
+ (apr_uint64_t)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_out > 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);
+ /* No need for cleanup any longer */
+ apr_pool_cleanup_kill(r->pool, ctx, deflate_ctx_cleanup);
+
+ /* 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_status_t rv;
+
+ /* flush the remaining data from the zlib buffers */
+ zRC = flush_libz_buffer(ctx, c, f->c->bucket_alloc, deflate,
+ Z_SYNC_FLUSH, NO_UPDATE_CRC);
+ if (zRC != Z_OK) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01385)
+ "Zlib error %d flushing zlib output buffer (%s)",
+ zRC, ctx->stream.msg);
+ return APR_EGENERAL;
+ }
+
+ /* Remove flush bucket from old brigade anf insert into the new. */
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+ rv = ap_pass_brigade(f->next, ctx->bb);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ continue;
+ }
+
+ if (APR_BUCKET_IS_METADATA(e)) {
+ /*
+ * Remove meta data bucket from old brigade and insert into the
+ * new.
+ */
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+ continue;
+ }
+
+ /* read */
+ apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
+ if (!len) {
+ apr_bucket_delete(e);
+ continue;
+ }
+ if (len > APR_INT32_MAX) {
+ apr_bucket_split(e, APR_INT32_MAX);
+ 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) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01386)
+ "Zlib error %d deflating data (%s)", zRC,
+ ctx->stream.msg);
+ return APR_EGENERAL;
+ }
+ }
+
+ apr_bucket_delete(e);
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t consume_zlib_flags(deflate_ctx *ctx,
+ const char **data, apr_size_t *len)
+{
+ if ((ctx->zlib_flags & EXTRA_FIELD)) {
+ /* Consume 2 bytes length prefixed data. */
+ if (ctx->consume_pos == 0) {
+ if (!*len) {
+ return APR_INCOMPLETE;
+ }
+ ctx->consume_len = (unsigned int)**data;
+ ctx->consume_pos++;
+ ++*data;
+ --*len;
+ }
+ if (ctx->consume_pos == 1) {
+ if (!*len) {
+ return APR_INCOMPLETE;
+ }
+ ctx->consume_len += ((unsigned int)**data) << 8;
+ ctx->consume_pos++;
+ ++*data;
+ --*len;
+ }
+ if (*len < ctx->consume_len) {
+ ctx->consume_len -= *len;
+ *len = 0;
+ return APR_INCOMPLETE;
+ }
+ *data += ctx->consume_len;
+ *len -= ctx->consume_len;
+
+ ctx->consume_len = ctx->consume_pos = 0;
+ ctx->zlib_flags &= ~EXTRA_FIELD;
+ }
+
+ if ((ctx->zlib_flags & ORIG_NAME)) {
+ /* Consume nul terminated string. */
+ while (*len && **data) {
+ ++*data;
+ --*len;
+ }
+ if (!*len) {
+ return APR_INCOMPLETE;
+ }
+ /* .. and nul. */
+ ++*data;
+ --*len;
+
+ ctx->zlib_flags &= ~ORIG_NAME;
+ }
+
+ if ((ctx->zlib_flags & COMMENT)) {
+ /* Consume nul terminated string. */
+ while (*len && **data) {
+ ++*data;
+ --*len;
+ }
+ if (!*len) {
+ return APR_INCOMPLETE;
+ }
+ /* .. and nul. */
+ ++*data;
+ --*len;
+
+ ctx->zlib_flags &= ~COMMENT;
+ }
+
+ if ((ctx->zlib_flags & HEAD_CRC)) {
+ /* Consume CRC16 (2 octets). */
+ if (ctx->consume_pos == 0) {
+ if (!*len) {
+ return APR_INCOMPLETE;
+ }
+ ctx->consume_pos++;
+ ++*data;
+ --*len;
+ }
+ if (!*len) {
+ return APR_INCOMPLETE;
+ }
+ ++*data;
+ --*len;
+
+ ctx->consume_pos = 0;
+ ctx->zlib_flags &= ~HEAD_CRC;
+ }
+
+ 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;
+ deflate_dirconf_t *dc;
+ apr_off_t inflate_limit;
+
+ /* 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);
+ dc = ap_get_module_config(r->per_dir_config, &deflate_module);
+
+ if (!ctx || ctx->header_len < sizeof(ctx->header)) {
+ apr_size_t len;
+
+ if (!ctx) {
+ /* only work on main request/no subrequests */
+ if (!ap_is_initial_req(r)) {
+ ap_remove_input_filter(f);
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
+
+ /* We can't operate on Content-Ranges */
+ if (apr_table_get(r->headers_in, "Content-Range") != NULL) {
+ ap_remove_input_filter(f);
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
+
+ /* Check whether request body is gzipped.
+ *
+ * If it is, we're transforming the contents, invalidating
+ * some request headers including Content-Encoding.
+ *
+ * If not, we just remove ourself.
+ */
+ if (check_gzip(r, r->headers_in, NULL) == 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);
+ }
+
+ do {
+ apr_brigade_cleanup(ctx->bb);
+
+ len = sizeof(ctx->header) - ctx->header_len;
+ rv = ap_get_brigade(f->next, ctx->bb, AP_MODE_READBYTES, block,
+ len);
+
+ /* ap_get_brigade may return success with an empty brigade for
+ * a non-blocking read which would block (an empty brigade for
+ * a blocking read is an issue which is simply forwarded here).
+ */
+ if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(ctx->bb)) {
+ return rv;
+ }
+
+ /* zero length body? step aside */
+ bkt = APR_BRIGADE_FIRST(ctx->bb);
+ if (APR_BUCKET_IS_EOS(bkt)) {
+ if (ctx->header_len) {
+ /* If the header was (partially) read it's an error, this
+ * is not a gzip Content-Encoding, as claimed.
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02619)
+ "Encountered premature end-of-stream while "
+ "reading inflate header");
+ return APR_EGENERAL;
+ }
+ APR_BUCKET_REMOVE(bkt);
+ APR_BRIGADE_INSERT_TAIL(bb, bkt);
+ ap_remove_input_filter(f);
+ return APR_SUCCESS;
+ }
+
+ rv = apr_brigade_flatten(ctx->bb,
+ ctx->header + ctx->header_len, &len);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ if (len && !ctx->header_len) {
+ apr_table_unset(r->headers_in, "Content-Length");
+ apr_table_unset(r->headers_in, "Content-MD5");
+ }
+ ctx->header_len += len;
+
+ } while (ctx->header_len < sizeof(ctx->header));
+
+ /* We didn't get the magic bytes. */
+ if (ctx->header[0] != deflate_magic[0] ||
+ ctx->header[1] != deflate_magic[1]) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01387)
+ "Zlib: Invalid header");
+ return APR_EGENERAL;
+ }
+
+ ctx->zlib_flags = ctx->header[3];
+ if ((ctx->zlib_flags & RESERVED)) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01388)
+ "Zlib: Invalid flags %02x", ctx->zlib_flags);
+ 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, APLOGNO(01389)
+ "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);
+ }
+
+ inflate_limit = dc->inflate_limit;
+ if (inflate_limit == 0) {
+ /* The core is checking the deflated body, we'll check the inflated */
+ inflate_limit = ap_get_limit_req_body(f->r);
+ }
+
+ if (APR_BRIGADE_EMPTY(ctx->proc_bb)) {
+ rv = ap_get_brigade(f->next, ctx->bb, mode, block, readbytes);
+
+ /* Don't terminate on EAGAIN (or success with an empty brigade in
+ * non-blocking mode), just return focus.
+ */
+ if (block == APR_NONBLOCK_READ
+ && (APR_STATUS_IS_EAGAIN(rv)
+ || (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(ctx->bb)))) {
+ return rv;
+ }
+ if (rv != APR_SUCCESS) {
+ inflateEnd(&ctx->stream);
+ return rv;
+ }
+
+ for (bkt = APR_BRIGADE_FIRST(ctx->bb);
+ bkt != APR_BRIGADE_SENTINEL(ctx->bb);
+ bkt = APR_BUCKET_NEXT(bkt))
+ {
+ const char *data;
+ apr_size_t len;
+
+ if (APR_BUCKET_IS_EOS(bkt)) {
+ if (!ctx->done) {
+ inflateEnd(&ctx->stream);
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02481)
+ "Encountered premature end-of-stream while inflating");
+ return APR_EGENERAL;
+ }
+
+ /* Move everything to the returning brigade. */
+ APR_BUCKET_REMOVE(bkt);
+ APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, bkt);
+ break;
+ }
+
+ if (APR_BUCKET_IS_FLUSH(bkt)) {
+ apr_bucket *tmp_b;
+
+ ctx->inflate_total += ctx->stream.avail_out;
+ zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH);
+ ctx->inflate_total -= ctx->stream.avail_out;
+ if (zRC != Z_OK) {
+ inflateEnd(&ctx->stream);
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01391)
+ "Zlib error %d inflating data (%s)", zRC,
+ ctx->stream.msg);
+ return APR_EGENERAL;
+ }
+
+ if (inflate_limit && ctx->inflate_total > inflate_limit) {
+ inflateEnd(&ctx->stream);
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02647)
+ "Inflated content length of %" APR_OFF_T_FMT
+ " is larger than the configured limit"
+ " of %" APR_OFF_T_FMT,
+ ctx->inflate_total, inflate_limit);
+ return APR_ENOSPC;
+ }
+
+ if (!check_ratio(r, ctx, dc)) {
+ inflateEnd(&ctx->stream);
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02805)
+ "Inflated content ratio is larger than the "
+ "configured limit %i by %i time(s)",
+ dc->ratio_limit, dc->ratio_burst);
+ return APR_EINVAL;
+ }
+
+ len = c->bufferSize - ctx->stream.avail_out;
+ ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
+ tmp_b = apr_bucket_heap_create((char *)ctx->buffer, len,
+ NULL, f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_b);
+
+ ctx->stream.next_out = ctx->buffer;
+ ctx->stream.avail_out = c->bufferSize;
+
+ /* Flush everything so far in the returning brigade, but continue
+ * reading should EOS/more follow (don't lose them).
+ */
+ tmp_b = APR_BUCKET_PREV(bkt);
+ APR_BUCKET_REMOVE(bkt);
+ APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, bkt);
+ bkt = tmp_b;
+ continue;
+ }
+
+ /* sanity check - data after completed compressed body and before eos? */
+ if (ctx->done) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02482)
+ "Encountered extra data after compressed data");
+ return APR_EGENERAL;
+ }
+
+ /* read */
+ apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ);
+ if (!len) {
+ continue;
+ }
+ if (len > APR_INT32_MAX) {
+ apr_bucket_split(bkt, APR_INT32_MAX);
+ apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ);
+ }
+
+ if (ctx->zlib_flags) {
+ rv = consume_zlib_flags(ctx, &data, &len);
+ if (rv == APR_SUCCESS) {
+ ctx->zlib_flags = 0;
+ }
+ if (!len) {
+ continue;
+ }
+ }
+
+ /* pass through zlib inflate. */
+ ctx->stream.next_in = (unsigned char *)data;
+ ctx->stream.avail_in = (int)len;
+
+ if (!ctx->validation_buffer) {
+ 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;
+ }
+
+ ctx->inflate_total += ctx->stream.avail_out;
+ zRC = inflate(&ctx->stream, Z_NO_FLUSH);
+ ctx->inflate_total -= ctx->stream.avail_out;
+ if (zRC != Z_OK && zRC != Z_STREAM_END) {
+ inflateEnd(&ctx->stream);
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01392)
+ "Zlib error %d inflating data (%s)", zRC,
+ ctx->stream.msg);
+ return APR_EGENERAL;
+ }
+
+ if (inflate_limit && ctx->inflate_total > inflate_limit) {
+ inflateEnd(&ctx->stream);
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02648)
+ "Inflated content length of %" APR_OFF_T_FMT
+ " is larger than the configured limit"
+ " of %" APR_OFF_T_FMT,
+ ctx->inflate_total, inflate_limit);
+ return APR_ENOSPC;
+ }
+
+ if (!check_ratio(r, ctx, dc)) {
+ inflateEnd(&ctx->stream);
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02649)
+ "Inflated content ratio is larger than the "
+ "configured limit %i by %i time(s)",
+ dc->ratio_limit, dc->ratio_burst);
+ return APR_EINVAL;
+ }
+
+ if (zRC == Z_STREAM_END) {
+ ctx->validation_buffer = apr_pcalloc(r->pool,
+ VALIDATION_SIZE);
+ ctx->validation_buffer_length = 0;
+ break;
+ }
+ }
+ }
+
+ if (ctx->validation_buffer) {
+ apr_bucket *tmp_heap;
+ apr_size_t avail, valid;
+ unsigned char *buf = ctx->validation_buffer;
+
+ avail = ctx->stream.avail_in;
+ valid = (apr_size_t)VALIDATION_SIZE -
+ ctx->validation_buffer_length;
+
+ /*
+ * We have inflated all data. Now try to capture the
+ * validation bytes. We may not have them all available
+ * right now, but capture what is there.
+ */
+ if (avail < valid) {
+ memcpy(buf + ctx->validation_buffer_length,
+ ctx->stream.next_in, avail);
+ ctx->validation_buffer_length += avail;
+ continue;
+ }
+ memcpy(buf + ctx->validation_buffer_length,
+ ctx->stream.next_in, valid);
+ ctx->validation_buffer_length += valid;
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01393)
+ "Zlib: Inflated %" APR_UINT64_T_FMT
+ " to %" APR_UINT64_T_FMT " : URL %s",
+ (apr_uint64_t)ctx->stream.total_in,
+ (apr_uint64_t)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;
+
+ {
+ unsigned long compCRC, compLen;
+ compCRC = getLong(buf);
+ if (ctx->crc != compCRC) {
+ inflateEnd(&ctx->stream);
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01394)
+ "Zlib: CRC error inflating data");
+ return APR_EGENERAL;
+ }
+ compLen = getLong(buf + VALIDATION_SIZE / 2);
+ /* gzip stores original size only as 4 byte value */
+ if ((ctx->stream.total_out & 0xFFFFFFFF) != compLen) {
+ inflateEnd(&ctx->stream);
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01395)
+ "Zlib: Length %" APR_UINT64_T_FMT
+ " of inflated data does not match"
+ " expected value %ld",
+ (apr_uint64_t)ctx->stream.total_out, compLen);
+ return APR_EGENERAL;
+ }
+ }
+
+ inflateEnd(&ctx->stream);
+
+ ctx->done = 1;
+
+ /* Did we have trailing data behind the closing 8 bytes? */
+ if (avail > valid) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02485)
+ "Encountered extra data after compressed data");
+ return APR_EGENERAL;
+ }
+ }
+
+ }
+ 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)) {
+ if (apr_brigade_partition(ctx->proc_bb, readbytes, &bkt) == APR_INCOMPLETE) {
+ APR_BRIGADE_CONCAT(bb, ctx->proc_bb);
+ }
+ else {
+ APR_BRIGADE_CONCAT(bb, ctx->proc_bb);
+ apr_brigade_split_ex(bb, bkt, ctx->proc_bb);
+ }
+ if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
+ ap_remove_input_filter(f);
+ }
+ }
+
+ return APR_SUCCESS;
+}
+
+
+/* Filter to inflate for a content-transforming proxy. */
+static apr_status_t inflate_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;
+ apr_status_t rv;
+ deflate_filter_config *c;
+ deflate_dirconf_t *dc;
+
+ /* Do nothing if asked to filter nothing. */
+ if (APR_BRIGADE_EMPTY(bb)) {
+ return APR_SUCCESS;
+ }
+
+ c = ap_get_module_config(r->server->module_config, &deflate_module);
+ dc = ap_get_module_config(r->per_dir_config, &deflate_module);
+
+ if (!ctx) {
+
+ /*
+ * Only work on main request, not subrequests,
+ * that are not a 204 response with no content
+ * and not a partial response to a Range request,
+ * and only when Content-Encoding ends in gzip.
+ *
+ * Note that responding to 304 is handled separately to
+ * set the required headers (such as ETag) per RFC7232, 4.1.
+ */
+ if (!ap_is_initial_req(r) || (r->status == HTTP_NO_CONTENT) ||
+ (apr_table_get(r->headers_out, "Content-Range") != NULL) ||
+ (check_gzip(r, r->headers_out, r->err_headers_out) == 0)
+ ) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ /*
+ * At this point we have decided to filter the content, so change
+ * important content metadata before sending any response out.
+ * Content-Encoding was already reset by the check_gzip() call.
+ */
+ apr_table_unset(r->headers_out, "Content-Length");
+ apr_table_unset(r->headers_out, "Content-MD5");
+ deflate_check_etag(r, "gunzip");
+
+ /* For a 304 response, only change the headers */
+ if (r->status == HTTP_NOT_MODIFIED) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
+ ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
+ ctx->buffer = apr_palloc(r->pool, c->bufferSize);
+ ctx->libz_end_func = inflateEnd;
+ ctx->validation_buffer = NULL;
+ ctx->validation_buffer_length = 0;
+
+ 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, APLOGNO(01397)
+ "unable to init Zlib: "
+ "inflateInit2 returned %d: URL %s",
+ zRC, r->uri);
+ /*
+ * Remove ourselves as it does not make sense to return:
+ * We are not able to init libz and pass data down the chain
+ * compressed.
+ */
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ /*
+ * Register a cleanup function to ensure that we cleanup the internal
+ * libz resources.
+ */
+ apr_pool_cleanup_register(r->pool, ctx, deflate_ctx_cleanup,
+ apr_pool_cleanup_null);
+
+ /* initialize inflate 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;
+
+ e = APR_BRIGADE_FIRST(bb);
+
+ if (APR_BUCKET_IS_EOS(e)) {
+ /*
+ * We are really done now. Ensure that we never return here, even
+ * if a second EOS bucket falls down the chain. Thus remove
+ * ourselves.
+ */
+ ap_remove_output_filter(f);
+ /* should be zero already anyway */
+ ctx->stream.avail_in = 0;
+ /*
+ * Flush the remaining data from the zlib buffers. It is correct
+ * to use Z_SYNC_FLUSH in this case and not Z_FINISH as in the
+ * deflate case. In the inflate case Z_FINISH requires to have a
+ * large enough output buffer to put ALL data in otherwise it
+ * fails, whereas in the deflate case you can empty a filled output
+ * buffer and call it again until no more output can be created.
+ */
+ flush_libz_buffer(ctx, c, f->c->bucket_alloc, inflate, Z_SYNC_FLUSH,
+ UPDATE_CRC);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01398)
+ "Zlib: Inflated %" APR_UINT64_T_FMT
+ " to %" APR_UINT64_T_FMT " : URL %s",
+ (apr_uint64_t)ctx->stream.total_in,
+ (apr_uint64_t)ctx->stream.total_out, r->uri);
+
+ if (ctx->validation_buffer_length == VALIDATION_SIZE) {
+ unsigned long compCRC, compLen;
+ compCRC = getLong(ctx->validation_buffer);
+ if (ctx->crc != compCRC) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01399)
+ "Zlib: Checksum of inflated stream invalid");
+ return APR_EGENERAL;
+ }
+ ctx->validation_buffer += VALIDATION_SIZE / 2;
+ compLen = getLong(ctx->validation_buffer);
+ /* gzip stores original size only as 4 byte value */
+ if ((ctx->stream.total_out & 0xFFFFFFFF) != compLen) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01400)
+ "Zlib: Length of inflated stream invalid");
+ return APR_EGENERAL;
+ }
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01401)
+ "Zlib: Validation bytes not present");
+ return APR_EGENERAL;
+ }
+
+ inflateEnd(&ctx->stream);
+ /* No need for cleanup any longer */
+ apr_pool_cleanup_kill(r->pool, ctx, deflate_ctx_cleanup);
+
+ /* 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_status_t rv;
+
+ /* flush the remaining data from the zlib buffers */
+ zRC = flush_libz_buffer(ctx, c, f->c->bucket_alloc, inflate,
+ Z_SYNC_FLUSH, UPDATE_CRC);
+ if (zRC == Z_STREAM_END) {
+ if (ctx->validation_buffer == NULL) {
+ ctx->validation_buffer = apr_pcalloc(f->r->pool,
+ VALIDATION_SIZE);
+ }
+ }
+ else if (zRC != Z_OK) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01402)
+ "Zlib error %d flushing inflate buffer (%s)",
+ zRC, ctx->stream.msg);
+ return APR_EGENERAL;
+ }
+
+ /* Remove flush bucket from old brigade anf insert into the new. */
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+ rv = ap_pass_brigade(f->next, ctx->bb);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ continue;
+ }
+
+ if (APR_BUCKET_IS_METADATA(e)) {
+ /*
+ * Remove meta data bucket from old brigade and insert into the
+ * new.
+ */
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+ continue;
+ }
+
+ /* read */
+ apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
+ if (!len) {
+ apr_bucket_delete(e);
+ continue;
+ }
+ if (len > APR_INT32_MAX) {
+ apr_bucket_split(e, APR_INT32_MAX);
+ apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
+ }
+
+ /* first bucket contains zlib header */
+ if (ctx->header_len < sizeof(ctx->header)) {
+ apr_size_t rem;
+
+ rem = sizeof(ctx->header) - ctx->header_len;
+ if (len < rem) {
+ memcpy(ctx->header + ctx->header_len, data, len);
+ ctx->header_len += len;
+ apr_bucket_delete(e);
+ continue;
+ }
+ memcpy(ctx->header + ctx->header_len, data, rem);
+ ctx->header_len += rem;
+ {
+ int zlib_method;
+ zlib_method = ctx->header[2];
+ if (zlib_method != Z_DEFLATED) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01404)
+ "inflate: data not deflated!");
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+ if (ctx->header[0] != deflate_magic[0] ||
+ ctx->header[1] != deflate_magic[1]) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01405)
+ "inflate: bad header");
+ return APR_EGENERAL ;
+ }
+ ctx->zlib_flags = ctx->header[3];
+ if ((ctx->zlib_flags & RESERVED)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02620)
+ "inflate: bad flags %02x",
+ ctx->zlib_flags);
+ return APR_EGENERAL;
+ }
+ }
+ if (len == rem) {
+ apr_bucket_delete(e);
+ continue;
+ }
+ data += rem;
+ len -= rem;
+ }
+
+ if (ctx->zlib_flags) {
+ rv = consume_zlib_flags(ctx, &data, &len);
+ if (rv == APR_SUCCESS) {
+ ctx->zlib_flags = 0;
+ }
+ if (!len) {
+ apr_bucket_delete(e);
+ continue;
+ }
+ }
+
+ /* pass through zlib inflate. */
+ ctx->stream.next_in = (unsigned char *)data;
+ ctx->stream.avail_in = len;
+
+ if (ctx->validation_buffer) {
+ if (ctx->validation_buffer_length < VALIDATION_SIZE) {
+ apr_size_t copy_size;
+
+ copy_size = VALIDATION_SIZE - ctx->validation_buffer_length;
+ if (copy_size > ctx->stream.avail_in)
+ copy_size = ctx->stream.avail_in;
+ memcpy(ctx->validation_buffer + ctx->validation_buffer_length,
+ ctx->stream.next_in, copy_size);
+ /* Saved copy_size bytes */
+ ctx->stream.avail_in -= copy_size;
+ ctx->validation_buffer_length += copy_size;
+ }
+ if (ctx->stream.avail_in) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01407)
+ "Zlib: %d bytes of garbage at the end of "
+ "compressed stream.", ctx->stream.avail_in);
+ /*
+ * There is nothing worth consuming for zlib left, because it is
+ * either garbage data or the data has been copied to the
+ * validation buffer (processing validation data is no business
+ * for zlib). So set ctx->stream.avail_in to zero to indicate
+ * this to the following while loop.
+ */
+ ctx->stream.avail_in = 0;
+ }
+ }
+
+ while (ctx->stream.avail_in != 0) {
+ if (ctx->stream.avail_out == 0) {
+ ctx->stream.next_out = ctx->buffer;
+ len = c->bufferSize - ctx->stream.avail_out;
+
+ ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
+ 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 = inflate(&ctx->stream, Z_NO_FLUSH);
+
+ if (zRC != Z_OK && zRC != Z_STREAM_END) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01409)
+ "Zlib error %d inflating data (%s)", zRC,
+ ctx->stream.msg);
+ return APR_EGENERAL;
+ }
+
+ if (!check_ratio(r, ctx, dc)) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02650)
+ "Inflated content ratio is larger than the "
+ "configured limit %i by %i time(s)",
+ dc->ratio_limit, dc->ratio_burst);
+ return APR_EINVAL;
+ }
+
+ if (zRC == Z_STREAM_END) {
+ /*
+ * We have inflated all data. Now try to capture the
+ * validation bytes. We may not have them all available
+ * right now, but capture what is there.
+ */
+ ctx->validation_buffer = apr_pcalloc(f->r->pool,
+ VALIDATION_SIZE);
+ if (ctx->stream.avail_in > VALIDATION_SIZE) {
+ ctx->validation_buffer_length = VALIDATION_SIZE;
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01408)
+ "Zlib: %d bytes of garbage at the end of "
+ "compressed stream.",
+ ctx->stream.avail_in - VALIDATION_SIZE);
+ }
+ else if (ctx->stream.avail_in > 0) {
+ ctx->validation_buffer_length = ctx->stream.avail_in;
+ }
+ if (ctx->validation_buffer_length)
+ memcpy(ctx->validation_buffer, ctx->stream.next_in,
+ ctx->validation_buffer_length);
+ break;
+ }
+ }
+
+ apr_bucket_delete(e);
+ }
+
+ return APR_SUCCESS;
+}
+
+static int mod_deflate_post_config(apr_pool_t *pconf, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ return OK;
+}
+
+
+#define PROTO_FLAGS AP_FILTER_PROTO_CHANGE|AP_FILTER_PROTO_CHANGE_LENGTH
+static void register_hooks(apr_pool_t *p)
+{
+ ap_register_output_filter(deflateFilterName, deflate_out_filter, NULL,
+ AP_FTYPE_CONTENT_SET);
+ ap_register_output_filter("INFLATE", inflate_out_filter, NULL,
+ AP_FTYPE_RESOURCE-1);
+ ap_register_input_filter(deflateFilterName, deflate_in_filter, NULL,
+ AP_FTYPE_CONTENT_SET);
+ ap_hook_post_config(mod_deflate_post_config, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+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)"),
+ AP_INIT_TAKE1("DeflateInflateLimitRequestBody", deflate_set_inflate_limit, NULL, OR_ALL,
+ "Set a limit on size of inflated input"),
+ AP_INIT_TAKE1("DeflateInflateRatioLimit", deflate_set_inflate_ratio_limit, NULL, OR_ALL,
+ "Set the inflate ratio limit above which inflation is "
+ "aborted (default: " APR_STRINGIFY(AP_INFLATE_RATIO_LIMIT) ")"),
+ AP_INIT_TAKE1("DeflateInflateRatioBurst", deflate_set_inflate_ratio_burst, NULL, OR_ALL,
+ "Set the maximum number of following inflate ratios above limit "
+ "(default: " APR_STRINGIFY(AP_INFLATE_RATIO_BURST) ")"),
+ {NULL}
+};
+
+AP_DECLARE_MODULE(deflate) = {
+ STANDARD20_MODULE_STUFF,
+ create_deflate_dirconf, /* 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/modules/filters/mod_deflate.dep b/modules/filters/mod_deflate.dep
new file mode 100644
index 0000000..4715df0
--- /dev/null
+++ b/modules/filters/mod_deflate.dep
@@ -0,0 +1,52 @@
+# Microsoft Developer Studio Generated Dependency File, included by mod_deflate.mak
+
+..\..\build\win32\httpd.rc : \
+ "..\..\include\ap_release.h"\
+
+
+.\mod_deflate.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ "..\ssl\mod_ssl.h"\
+
diff --git a/modules/filters/mod_deflate.dsp b/modules/filters/mod_deflate.dsp
new file mode 100644
index 0000000..f990c97
--- /dev/null
+++ b/modules/filters/mod_deflate.dsp
@@ -0,0 +1,111 @@
+# 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" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../ssl" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/zlib" /D "NDEBUG" /D "ZLIB_DLL" /D "HAVE_ZUTIL_H" /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 /fo"Release/mod_deflate.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_deflate.so" /d LONG_NAME="deflate_module for Apache"
+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_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
+# Begin Special Build Tool
+TargetPath=.\Release\mod_deflate.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!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 "../ssl" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/zlib" /D "_DEBUG" /D "ZLIB_DLL" /D "HAVE_ZUTIL_H" /D "WIN32" /D "_WINDOWS" /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 /fo"Debug/mod_deflate.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_deflate.so" /d LONG_NAME="deflate_module for Apache"
+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
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_deflate.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!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=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
diff --git a/modules/filters/mod_deflate.exp b/modules/filters/mod_deflate.exp
new file mode 100644
index 0000000..9ec7688
--- /dev/null
+++ b/modules/filters/mod_deflate.exp
@@ -0,0 +1 @@
+deflate_module
diff --git a/modules/filters/mod_deflate.mak b/modules/filters/mod_deflate.mak
new file mode 100644
index 0000000..9579284
--- /dev/null
+++ b/modules/filters/mod_deflate.mak
@@ -0,0 +1,353 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on mod_deflate.dsp
+!IF "$(CFG)" == ""
+CFG=mod_deflate - Win32 Release
+!MESSAGE No configuration specified. Defaulting to mod_deflate - Win32 Release.
+!ENDIF
+
+!IF "$(CFG)" != "mod_deflate - Win32 Release" && "$(CFG)" != "mod_deflate - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!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
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(CFG)" == "mod_deflate - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_deflate.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_deflate.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_deflate.obj"
+ -@erase "$(INTDIR)\mod_deflate.res"
+ -@erase "$(INTDIR)\mod_deflate_src.idb"
+ -@erase "$(INTDIR)\mod_deflate_src.pdb"
+ -@erase "$(OUTDIR)\mod_deflate.exp"
+ -@erase "$(OUTDIR)\mod_deflate.lib"
+ -@erase "$(OUTDIR)\mod_deflate.pdb"
+ -@erase "$(OUTDIR)\mod_deflate.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../ssl" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/zlib" /D "NDEBUG" /D "ZLIB_DLL" /D "HAVE_ZUTIL_H" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_deflate_src" /FD /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_deflate.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_deflate.so" /d LONG_NAME="deflate_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_deflate.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib zdll.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_deflate.pdb" /debug /out:"$(OUTDIR)\mod_deflate.so" /implib:"$(OUTDIR)\mod_deflate.lib" /libpath:"../../srclib/zlib" /base:@..\..\os\win32\BaseAddr.ref,mod_deflate.so /opt:ref
+LINK32_OBJS= \
+ "$(INTDIR)\mod_deflate.obj" \
+ "$(INTDIR)\mod_deflate.res" \
+ "..\..\srclib\apr\Release\libapr-1.lib" \
+ "..\..\srclib\apr-util\Release\libaprutil-1.lib" \
+ "..\..\Release\libhttpd.lib"
+
+"$(OUTDIR)\mod_deflate.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Release\mod_deflate.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_deflate.so"
+ if exist .\Release\mod_deflate.so.manifest mt.exe -manifest .\Release\mod_deflate.so.manifest -outputresource:.\Release\mod_deflate.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ELSEIF "$(CFG)" == "mod_deflate - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_deflate.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_deflate.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_deflate.obj"
+ -@erase "$(INTDIR)\mod_deflate.res"
+ -@erase "$(INTDIR)\mod_deflate_src.idb"
+ -@erase "$(INTDIR)\mod_deflate_src.pdb"
+ -@erase "$(OUTDIR)\mod_deflate.exp"
+ -@erase "$(OUTDIR)\mod_deflate.lib"
+ -@erase "$(OUTDIR)\mod_deflate.pdb"
+ -@erase "$(OUTDIR)\mod_deflate.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../ssl" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/zlib" /D "_DEBUG" /D "ZLIB_DLL" /D "HAVE_ZUTIL_H" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_deflate_src" /FD /EHsc /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_deflate.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_deflate.so" /d LONG_NAME="deflate_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_deflate.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib zdll.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_deflate.pdb" /debug /out:"$(OUTDIR)\mod_deflate.so" /implib:"$(OUTDIR)\mod_deflate.lib" /libpath:"../../srclib/zlib" /base:@..\..\os\win32\BaseAddr.ref,mod_deflate.so
+LINK32_OBJS= \
+ "$(INTDIR)\mod_deflate.obj" \
+ "$(INTDIR)\mod_deflate.res" \
+ "..\..\srclib\apr\Debug\libapr-1.lib" \
+ "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \
+ "..\..\Debug\libhttpd.lib"
+
+"$(OUTDIR)\mod_deflate.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Debug\mod_deflate.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_deflate.so"
+ if exist .\Debug\mod_deflate.so.manifest mt.exe -manifest .\Debug\mod_deflate.so.manifest -outputresource:.\Debug\mod_deflate.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+
+!IF "$(NO_EXTERNAL_DEPS)" != "1"
+!IF EXISTS("mod_deflate.dep")
+!INCLUDE "mod_deflate.dep"
+!ELSE
+!MESSAGE Warning: cannot find "mod_deflate.dep"
+!ENDIF
+!ENDIF
+
+
+!IF "$(CFG)" == "mod_deflate - Win32 Release" || "$(CFG)" == "mod_deflate - Win32 Debug"
+
+!IF "$(CFG)" == "mod_deflate - Win32 Release"
+
+"libapr - Win32 Release" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_deflate - Win32 Debug"
+
+"libapr - Win32 Debug" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_deflate - Win32 Release"
+
+"libaprutil - Win32 Release" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_deflate - Win32 Debug"
+
+"libaprutil - Win32 Debug" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_deflate - Win32 Release"
+
+"libhttpd - Win32 Release" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 ReleaseCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_deflate - Win32 Debug"
+
+"libhttpd - Win32 Debug" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 DebugCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ENDIF
+
+SOURCE=..\..\build\win32\httpd.rc
+
+!IF "$(CFG)" == "mod_deflate - Win32 Release"
+
+
+"$(INTDIR)\mod_deflate.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_deflate.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_deflate.so" /d LONG_NAME="deflate_module for Apache" $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "mod_deflate - Win32 Debug"
+
+
+"$(INTDIR)\mod_deflate.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_deflate.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_deflate.so" /d LONG_NAME="deflate_module for Apache" $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\mod_deflate.c
+
+"$(INTDIR)\mod_deflate.obj" : $(SOURCE) "$(INTDIR)"
+
+
+
+!ENDIF
+
diff --git a/modules/filters/mod_ext_filter.c b/modules/filters/mod_ext_filter.c
new file mode 100644
index 0000000..7afd8dd
--- /dev/null
+++ b/modules/filters/mod_ext_filter.c
@@ -0,0 +1,954 @@
+/* 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"
+
+#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 log_stderr;
+ int onfail;
+} 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, hit_eos;
+#if APR_FILES_AS_SOCKETS
+ apr_pollset_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 *);
+static apr_status_t ef_input_filter(ap_filter_t *, apr_bucket_brigade *,
+ ap_input_mode_t, apr_read_type_e,
+ apr_off_t);
+
+#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->log_stderr = -1;
+ dc->onfail = -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->log_stderr != -1) { /* if admin coded something... */
+ a->log_stderr = over->log_stderr;
+ }
+ else {
+ a->log_stderr = base->log_stderr;
+ }
+
+ if (over->onfail != -1) { /* if admin coded something... */
+ a->onfail = over->onfail;
+ }
+ else {
+ a->onfail = base->onfail;
+ }
+
+ return a;
+}
+
+static const char *add_options(cmd_parms *cmd, void *in_dc,
+ const char *arg)
+{
+ ef_dir_t *dc = in_dc;
+
+ if (!strcasecmp(arg, "LogStderr")) {
+ dc->log_stderr = 1;
+ }
+ else if (!strcasecmp(arg, "NoLogStderr")) {
+ dc->log_stderr = 0;
+ }
+ else if (!strcasecmp(arg, "Onfail=remove")) {
+ dc->onfail = 1;
+ }
+ else if (!strcasecmp(arg, "Onfail=abort")) {
+ dc->onfail = 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;
+ char *normalized_name;
+ ef_filter_t *filter;
+
+ name = ap_getword_white(cmd->pool, &args);
+ if (!name) {
+ return "Filter name not found";
+ }
+
+ /* During request processing, we find information about the filter
+ * by looking up the filter name provided by core server in our
+ * hash table. But the core server has normalized the filter
+ * name by converting it to lower case. Thus, when adding the
+ * filter to our hash table we have to use lower case as well.
+ */
+ normalized_name = apr_pstrdup(cmd->pool, name);
+ ap_str_tolower(normalized_name);
+
+ if (apr_hash_get(conf->h, normalized_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, normalized_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);
+ }
+ 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, filter->ftype);
+ }
+ 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: 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_core_module_config(r->per_dir_config);
+ 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 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)%pm: %s\n",
+ time_str,
+ r->useragent_ip,
+ err,
+ &err,
+ 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);
+
+ rc = apr_procattr_error_check_set(ctx->procattr, 1);
+ if (rc != APR_SUCCESS) {
+ return rc;
+ }
+
+ /* 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, APLOGNO(01458)
+ "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_pollfd_t pfd = { 0 };
+
+ rc = apr_pollset_create(&ctx->pollset, 2, ctx->p, 0);
+ ap_assert(rc == APR_SUCCESS);
+
+ pfd.p = ctx->p;
+ pfd.desc_type = APR_POLL_FILE;
+ pfd.reqevents = APR_POLLOUT;
+ pfd.desc.f = ctx->proc->in;
+ rc = apr_pollset_add(ctx->pollset, &pfd);
+ ap_assert(rc == APR_SUCCESS);
+
+ pfd.reqevents = APR_POLLIN;
+ pfd.desc.f = ctx->proc->out;
+ rc = apr_pollset_add(ctx->pollset, &pfd);
+ 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 *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 ExtFilterInType %s "
+ "ExtFilterOuttype %s",
+ 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, APLOGNO(01459)
+ "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) {
+ const char *ctypes;
+
+ if (ctx->filter->mode == INPUT_FILTER) {
+ ctypes = apr_table_get(f->r->headers_in, "Content-Type");
+ }
+ else {
+ ctypes = f->r->content_type;
+ }
+
+ if (ctypes) {
+ 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;
+ }
+ }
+ else {
+ 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 (APLOGrtrace1(f->r)) {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 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 append it
+ * to the bucket brigade
+ */
+static apr_status_t drain_available_output(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_size_t len;
+ char buf[4096];
+ apr_status_t rv;
+ apr_bucket *b;
+
+ while (1) {
+ int lvl = APLOG_TRACE5;
+ len = sizeof(buf);
+ rv = apr_file_read(ctx->proc->out, buf, &len);
+ if (rv && !APR_STATUS_IS_EAGAIN(rv))
+ lvl = APLOG_DEBUG;
+ ap_log_rerror(APLOG_MARK, lvl, rv, r, APLOGNO(01460)
+ "apr_file_read(child output), len %" APR_SIZE_T_FMT, len);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ b = apr_bucket_heap_create(buf, len, NULL, c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+ return APR_SUCCESS;
+ }
+ /* 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, apr_bucket_brigade *bb)
+{
+ ef_ctx_t *ctx = f->ctx;
+ apr_status_t rv;
+ apr_size_t bytes_written = 0;
+ apr_size_t tmplen;
+
+ do {
+ tmplen = len - bytes_written;
+ rv = apr_file_write_full(ctx->proc->in,
+ (const char *)data + bytes_written,
+ tmplen, &tmplen);
+ bytes_written += tmplen;
+ if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, APLOGNO(01461)
+ "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, bb);
+ if (APR_STATUS_IS_EAGAIN(rv)) {
+#if APR_FILES_AS_SOCKETS
+ int num_events;
+ const apr_pollfd_t *pdesc;
+
+ rv = apr_pollset_poll(ctx->pollset, f->r->server->timeout,
+ &num_events, &pdesc);
+ if (rv != APR_SUCCESS && !APR_STATUS_IS_EINTR(rv)) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, f->r, APLOGNO(01462)
+ "apr_pollset_poll()");
+ /* some error such as APR_TIMEUP */
+ return rv;
+ }
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE6, rv, f->r,
+ "apr_pollset_poll()");
+#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(apr_time_from_msec(100));
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE6, 0, f->r, "apr_sleep()");
+#endif /* APR_FILES_AS_SOCKETS */
+ }
+ else if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ }
+ } while (bytes_written < len);
+ return rv;
+}
+
+/* ef_unified_filter:
+ *
+ * runs the bucket brigade bb through the filter and puts the result into
+ * bb, dropping the previous content of bb (the input)
+ */
+
+static int ef_unified_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;
+ apr_size_t len;
+ const char *data;
+ apr_status_t rv;
+ char buf[4096];
+ apr_bucket *eos = NULL;
+ apr_bucket_brigade *bb_tmp;
+
+ bb_tmp = apr_brigade_create(r->pool, c->bucket_alloc);
+
+ for (b = APR_BRIGADE_FIRST(bb);
+ b != APR_BRIGADE_SENTINEL(bb);
+ b = APR_BUCKET_NEXT(b))
+ {
+ if (APR_BUCKET_IS_EOS(b)) {
+ eos = b;
+ break;
+ }
+
+ if (AP_BUCKET_IS_ERROR(b)) {
+ apr_bucket *cpy;
+ apr_bucket_copy(b, &cpy);
+ APR_BRIGADE_INSERT_TAIL(bb_tmp, cpy);
+ break;
+ }
+
+ rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
+ if (rv != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01463) "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, bb_tmp))
+ != APR_SUCCESS) {
+ return rv;
+ }
+ }
+
+ apr_brigade_cleanup(bb);
+ APR_BRIGADE_CONCAT(bb, bb_tmp);
+ apr_brigade_destroy(bb_tmp);
+
+ 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, APLOGNO(01464)
+ "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, APLOGNO(01465)
+ "apr_file_pipe_timeout_set(child output)");
+ return rv;
+ }
+ }
+
+ do {
+ int lvl = APLOG_TRACE6;
+ len = sizeof(buf);
+ rv = apr_file_read(ctx->proc->out, buf, &len);
+ if (rv && !APR_STATUS_IS_EOF(rv) && !APR_STATUS_IS_EAGAIN(rv))
+ lvl = APLOG_ERR;
+ ap_log_rerror(APLOG_MARK, lvl, rv, r, APLOGNO(01466)
+ "apr_file_read(child output), len %" APR_SIZE_T_FMT, len);
+ 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) {
+ b = apr_bucket_heap_create(buf, len, NULL, c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+ }
+ } while (rv == APR_SUCCESS);
+
+ if (!APR_STATUS_IS_EOF(rv)) {
+ return rv;
+ }
+
+ if (eos) {
+ b = apr_bucket_eos_create(c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+ ctx->hit_eos = 1;
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t ef_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
+{
+ request_rec *r = f->r;
+ ef_ctx_t *ctx = f->ctx;
+ apr_status_t rv;
+
+ if (!ctx) {
+ if ((rv = init_filter_instance(f)) != APR_SUCCESS) {
+ ctx = f->ctx;
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01467)
+ "can't initialise output filter %s: %s",
+ f->frec->name,
+ (ctx->dc->onfail == 1) ? "removing" : "aborting");
+ ap_remove_output_filter(f);
+ if (ctx->dc->onfail == 1) {
+ return ap_pass_brigade(f->next, bb);
+ }
+ else {
+ apr_bucket *e;
+ f->r->status_line = "500 Internal Server Error";
+
+ apr_brigade_cleanup(bb);
+ e = ap_bucket_error_create(HTTP_INTERNAL_SERVER_ERROR,
+ NULL, r->pool,
+ f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, e);
+ e = apr_bucket_eos_create(f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, e);
+ ap_pass_brigade(f->next, bb);
+ return AP_FILTER_ERROR;
+ }
+ }
+ ctx = f->ctx;
+ }
+ if (ctx->noop) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ rv = ef_unified_filter(f, bb);
+ if (rv != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01468)
+ "ef_unified_filter() failed");
+ }
+
+ if ((rv = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(01469)
+ "ap_pass_brigade() failed");
+ }
+ return rv;
+}
+
+static apr_status_t 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)
+{
+ ef_ctx_t *ctx = f->ctx;
+ apr_status_t rv;
+
+ /* 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);
+ }
+
+ if (!ctx) {
+ if ((rv = init_filter_instance(f)) != APR_SUCCESS) {
+ ctx = f->ctx;
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, APLOGNO(01470)
+ "can't initialise input filter %s: %s",
+ f->frec->name,
+ (ctx->dc->onfail == 1) ? "removing" : "aborting");
+ ap_remove_input_filter(f);
+ if (ctx->dc->onfail == 1) {
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
+ else {
+ f->r->status = HTTP_INTERNAL_SERVER_ERROR;
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ }
+ ctx = f->ctx;
+ }
+
+ if (ctx->hit_eos) {
+ /* Match behaviour of HTTP_IN if filter is re-invoked after
+ * hitting EOS: give back another EOS. */
+ apr_bucket *e = apr_bucket_eos_create(f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, e);
+ return APR_SUCCESS;
+ }
+
+ if (ctx->noop) {
+ ap_remove_input_filter(f);
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
+
+ rv = ap_get_brigade(f->next, bb, mode, block, readbytes);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ rv = ef_unified_filter(f, bb);
+ return rv;
+}
+
+AP_DECLARE_MODULE(ext_filter) =
+{
+ STANDARD20_MODULE_STUFF,
+ create_ef_dir_conf,
+ merge_ef_dir_conf,
+ create_ef_server_conf,
+ NULL,
+ cmds,
+ register_hooks
+};
diff --git a/modules/filters/mod_ext_filter.dep b/modules/filters/mod_ext_filter.dep
new file mode 100644
index 0000000..6b66202
--- /dev/null
+++ b/modules/filters/mod_ext_filter.dep
@@ -0,0 +1,58 @@
+# Microsoft Developer Studio Generated Dependency File, included by mod_ext_filter.mak
+
+..\..\build\win32\httpd.rc : \
+ "..\..\include\ap_release.h"\
+
+
+.\mod_ext_filter.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\include\util_script.h"\
+ "..\..\include\util_time.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+
diff --git a/modules/filters/mod_ext_filter.dsp b/modules/filters/mod_ext_filter.dsp
new file mode 100644
index 0000000..f87d5d7
--- /dev/null
+++ b/modules/filters/mod_ext_filter.dsp
@@ -0,0 +1,111 @@
+# 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 /O2 /Oy- /Zi /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 /fo"Release/mod_ext_filter.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_ext_filter.so" /d LONG_NAME="ext_filter_module for Apache"
+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
+# Begin Special Build Tool
+TargetPath=.\Release\mod_ext_filter.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!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 /fo"Debug/mod_ext_filter.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_ext_filter.so" /d LONG_NAME="ext_filter_module for Apache"
+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
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_ext_filter.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!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=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
diff --git a/modules/filters/mod_ext_filter.exp b/modules/filters/mod_ext_filter.exp
new file mode 100644
index 0000000..ed3b8fc
--- /dev/null
+++ b/modules/filters/mod_ext_filter.exp
@@ -0,0 +1 @@
+ext_filter_module
diff --git a/modules/filters/mod_ext_filter.mak b/modules/filters/mod_ext_filter.mak
new file mode 100644
index 0000000..f03bff3
--- /dev/null
+++ b/modules/filters/mod_ext_filter.mak
@@ -0,0 +1,353 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on mod_ext_filter.dsp
+!IF "$(CFG)" == ""
+CFG=mod_ext_filter - Win32 Release
+!MESSAGE No configuration specified. Defaulting to mod_ext_filter - Win32 Release.
+!ENDIF
+
+!IF "$(CFG)" != "mod_ext_filter - Win32 Release" && "$(CFG)" != "mod_ext_filter - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!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
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(CFG)" == "mod_ext_filter - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_ext_filter.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_ext_filter.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_ext_filter.obj"
+ -@erase "$(INTDIR)\mod_ext_filter.res"
+ -@erase "$(INTDIR)\mod_ext_filter_src.idb"
+ -@erase "$(INTDIR)\mod_ext_filter_src.pdb"
+ -@erase "$(OUTDIR)\mod_ext_filter.exp"
+ -@erase "$(OUTDIR)\mod_ext_filter.lib"
+ -@erase "$(OUTDIR)\mod_ext_filter.pdb"
+ -@erase "$(OUTDIR)\mod_ext_filter.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_ext_filter_src" /FD /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_ext_filter.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_ext_filter.so" /d LONG_NAME="ext_filter_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_ext_filter.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_ext_filter.pdb" /debug /out:"$(OUTDIR)\mod_ext_filter.so" /implib:"$(OUTDIR)\mod_ext_filter.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_ext_filter.so /opt:ref
+LINK32_OBJS= \
+ "$(INTDIR)\mod_ext_filter.obj" \
+ "$(INTDIR)\mod_ext_filter.res" \
+ "..\..\srclib\apr\Release\libapr-1.lib" \
+ "..\..\srclib\apr-util\Release\libaprutil-1.lib" \
+ "..\..\Release\libhttpd.lib"
+
+"$(OUTDIR)\mod_ext_filter.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Release\mod_ext_filter.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_ext_filter.so"
+ if exist .\Release\mod_ext_filter.so.manifest mt.exe -manifest .\Release\mod_ext_filter.so.manifest -outputresource:.\Release\mod_ext_filter.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ELSEIF "$(CFG)" == "mod_ext_filter - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_ext_filter.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_ext_filter.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_ext_filter.obj"
+ -@erase "$(INTDIR)\mod_ext_filter.res"
+ -@erase "$(INTDIR)\mod_ext_filter_src.idb"
+ -@erase "$(INTDIR)\mod_ext_filter_src.pdb"
+ -@erase "$(OUTDIR)\mod_ext_filter.exp"
+ -@erase "$(OUTDIR)\mod_ext_filter.lib"
+ -@erase "$(OUTDIR)\mod_ext_filter.pdb"
+ -@erase "$(OUTDIR)\mod_ext_filter.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_ext_filter_src" /FD /EHsc /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_ext_filter.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_ext_filter.so" /d LONG_NAME="ext_filter_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_ext_filter.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_ext_filter.pdb" /debug /out:"$(OUTDIR)\mod_ext_filter.so" /implib:"$(OUTDIR)\mod_ext_filter.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_ext_filter.so
+LINK32_OBJS= \
+ "$(INTDIR)\mod_ext_filter.obj" \
+ "$(INTDIR)\mod_ext_filter.res" \
+ "..\..\srclib\apr\Debug\libapr-1.lib" \
+ "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \
+ "..\..\Debug\libhttpd.lib"
+
+"$(OUTDIR)\mod_ext_filter.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Debug\mod_ext_filter.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_ext_filter.so"
+ if exist .\Debug\mod_ext_filter.so.manifest mt.exe -manifest .\Debug\mod_ext_filter.so.manifest -outputresource:.\Debug\mod_ext_filter.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+
+!IF "$(NO_EXTERNAL_DEPS)" != "1"
+!IF EXISTS("mod_ext_filter.dep")
+!INCLUDE "mod_ext_filter.dep"
+!ELSE
+!MESSAGE Warning: cannot find "mod_ext_filter.dep"
+!ENDIF
+!ENDIF
+
+
+!IF "$(CFG)" == "mod_ext_filter - Win32 Release" || "$(CFG)" == "mod_ext_filter - Win32 Debug"
+
+!IF "$(CFG)" == "mod_ext_filter - Win32 Release"
+
+"libapr - Win32 Release" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_ext_filter - Win32 Debug"
+
+"libapr - Win32 Debug" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_ext_filter - Win32 Release"
+
+"libaprutil - Win32 Release" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_ext_filter - Win32 Debug"
+
+"libaprutil - Win32 Debug" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_ext_filter - Win32 Release"
+
+"libhttpd - Win32 Release" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 ReleaseCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_ext_filter - Win32 Debug"
+
+"libhttpd - Win32 Debug" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 DebugCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ENDIF
+
+SOURCE=..\..\build\win32\httpd.rc
+
+!IF "$(CFG)" == "mod_ext_filter - Win32 Release"
+
+
+"$(INTDIR)\mod_ext_filter.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_ext_filter.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_ext_filter.so" /d LONG_NAME="ext_filter_module for Apache" $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "mod_ext_filter - Win32 Debug"
+
+
+"$(INTDIR)\mod_ext_filter.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_ext_filter.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_ext_filter.so" /d LONG_NAME="ext_filter_module for Apache" $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\mod_ext_filter.c
+
+"$(INTDIR)\mod_ext_filter.obj" : $(SOURCE) "$(INTDIR)"
+
+
+
+!ENDIF
+
diff --git a/modules/filters/mod_filter.c b/modules/filters/mod_filter.c
new file mode 100644
index 0000000..5b5ecf6
--- /dev/null
+++ b/modules/filters/mod_filter.c
@@ -0,0 +1,767 @@
+/* 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.
+ */
+
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include "apr_hash.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_log.h"
+#include "util_filter.h"
+#include "ap_expr.h"
+
+module AP_MODULE_DECLARE_DATA filter_module;
+
+/**
+ * @brief is a filter provider, as defined and implemented by mod_filter.
+ *
+ * The struct is a linked list, with dispatch criteria
+ * defined for each filter. The provider implementation itself is a
+ * (2.0-compatible) ap_filter_rec_t* frec.
+ */
+struct ap_filter_provider_t {
+ ap_expr_info_t *expr;
+ const char **types;
+
+ /** The filter that implements this provider */
+ ap_filter_rec_t *frec;
+
+ /** The next provider in the list */
+ ap_filter_provider_t *next;
+};
+
+/** we need provider_ctx to save ctx values set by providers in filter_init */
+typedef struct provider_ctx provider_ctx;
+struct provider_ctx {
+ ap_filter_provider_t *provider;
+ void *ctx;
+ provider_ctx *next;
+};
+typedef struct {
+ ap_out_filter_func func;
+ void *fctx;
+ provider_ctx *init_ctx;
+} harness_ctx;
+
+typedef struct mod_filter_chain {
+ const char *fname;
+ struct mod_filter_chain *next;
+} mod_filter_chain;
+
+typedef struct {
+ apr_hash_t *live_filters;
+ mod_filter_chain *chain;
+} mod_filter_cfg;
+
+typedef struct {
+ const char* range ;
+} mod_filter_ctx ;
+
+
+static void filter_trace(conn_rec *c, int debug, const char *fname,
+ apr_bucket_brigade *bb)
+{
+ apr_bucket *b;
+
+ switch (debug) {
+ case 0: /* normal, operational use */
+ return;
+ case 1: /* mod_diagnostics level */
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01375) "%s", fname);
+ for (b = APR_BRIGADE_FIRST(bb);
+ b != APR_BRIGADE_SENTINEL(bb);
+ b = APR_BUCKET_NEXT(b)) {
+
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01376)
+ "%s: type: %s, length: %" APR_SIZE_T_FMT,
+ fname, b->type->name ? b->type->name : "(unknown)",
+ b->length);
+ }
+ break;
+ }
+}
+
+static int filter_init(ap_filter_t *f)
+{
+ ap_filter_provider_t *p;
+ provider_ctx *pctx;
+ int err;
+ ap_filter_rec_t *filter = f->frec;
+
+ harness_ctx *fctx = apr_pcalloc(f->r->pool, sizeof(harness_ctx));
+ for (p = filter->providers; p; p = p->next) {
+ if (p->frec->filter_init_func == filter_init) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(01377)
+ "Chaining of FilterProviders not supported");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ else if (p->frec->filter_init_func) {
+ f->ctx = NULL;
+ if ((err = p->frec->filter_init_func(f)) != OK) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(01378)
+ "filter_init for %s failed", p->frec->name);
+ return err; /* if anyone errors out here, so do we */
+ }
+ if (f->ctx != NULL) {
+ /* the filter init function set a ctx - we need to record it */
+ pctx = apr_pcalloc(f->r->pool, sizeof(provider_ctx));
+ pctx->provider = p;
+ pctx->ctx = f->ctx;
+ pctx->next = fctx->init_ctx;
+ fctx->init_ctx = pctx;
+ }
+ }
+ }
+ f->ctx = fctx;
+ return OK;
+}
+
+static int filter_lookup(ap_filter_t *f, ap_filter_rec_t *filter)
+{
+ ap_filter_provider_t *provider;
+ int match = 0;
+ const char *err = NULL;
+ request_rec *r = f->r;
+ harness_ctx *ctx = f->ctx;
+ provider_ctx *pctx;
+#ifndef NO_PROTOCOL
+ unsigned int proto_flags;
+ mod_filter_ctx *rctx = ap_get_module_config(r->request_config,
+ &filter_module);
+#endif
+
+ /* Check registered providers in order */
+ for (provider = filter->providers; provider; provider = provider->next) {
+ if (provider->expr) {
+ match = ap_expr_exec(r, provider->expr, &err);
+ if (err) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01379)
+ "Error evaluating filter dispatch condition: %s",
+ err);
+ match = 0;
+ }
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
+ "Expression condition for '%s' %s",
+ provider->frec->name,
+ match ? "matched" : "did not match");
+ }
+ else if (r->content_type) {
+ const char **type = provider->types;
+ size_t len = strcspn(r->content_type, "; \t");
+ AP_DEBUG_ASSERT(type != NULL);
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
+ "Content-Type '%s' ...", r->content_type);
+ while (*type) {
+ /* Handle 'content-type;charset=...' correctly */
+ if (strncmp(*type, r->content_type, len) == 0
+ && (*type)[len] == '\0') {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
+ "... matched '%s'", *type);
+ match = 1;
+ break;
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
+ "... did not match '%s'", *type);
+ }
+ type++;
+ }
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
+ "Content-Type condition for '%s' %s",
+ provider->frec->name,
+ match ? "matched" : "did not match");
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
+ "Content-Type condition for '%s' did not match: "
+ "no Content-Type", provider->frec->name);
+ }
+
+ if (match) {
+ /* condition matches this provider */
+#ifndef NO_PROTOCOL
+ /* check protocol
+ *
+ * FIXME:
+ * This is a quick hack and almost certainly buggy.
+ * The idea is that by putting this in mod_filter, we relieve
+ * filter implementations of the burden of fixing up HTTP headers
+ * for cases that are routinely affected by filters.
+ *
+ * Default is ALWAYS to do nothing, so as not to tread on the
+ * toes of filters which want to do it themselves.
+ *
+ */
+ proto_flags = provider->frec->proto_flags;
+
+ /* some specific things can't happen in a proxy */
+ if (r->proxyreq) {
+ if (proto_flags & AP_FILTER_PROTO_NO_PROXY) {
+ /* can't use this provider; try next */
+ continue;
+ }
+
+ if (proto_flags & AP_FILTER_PROTO_TRANSFORM) {
+ const char *str = apr_table_get(r->headers_out,
+ "Cache-Control");
+ if (str) {
+ if (ap_strcasestr(str, "no-transform")) {
+ /* can't use this provider; try next */
+ continue;
+ }
+ }
+ apr_table_addn(r->headers_out, "Warning",
+ apr_psprintf(r->pool,
+ "214 %s Transformation applied",
+ r->hostname));
+ }
+ }
+
+ /* things that are invalidated if the filter transforms content */
+ if (proto_flags & AP_FILTER_PROTO_CHANGE) {
+ apr_table_unset(r->headers_out, "Content-MD5");
+ apr_table_unset(r->headers_out, "ETag");
+ if (proto_flags & AP_FILTER_PROTO_CHANGE_LENGTH) {
+ apr_table_unset(r->headers_out, "Content-Length");
+ }
+ }
+
+ /* no-cache is for a filter that has different effect per-hit */
+ if (proto_flags & AP_FILTER_PROTO_NO_CACHE) {
+ apr_table_unset(r->headers_out, "Last-Modified");
+ apr_table_addn(r->headers_out, "Cache-Control", "no-cache");
+ }
+
+ if (proto_flags & AP_FILTER_PROTO_NO_BYTERANGE) {
+ apr_table_setn(r->headers_out, "Accept-Ranges", "none");
+ }
+ else if (rctx && rctx->range) {
+ /* restore range header we saved earlier */
+ apr_table_setn(r->headers_in, "Range", rctx->range);
+ rctx->range = NULL;
+ }
+#endif
+ for (pctx = ctx->init_ctx; pctx; pctx = pctx->next) {
+ if (pctx->provider == provider) {
+ ctx->fctx = pctx->ctx ;
+ }
+ }
+ ctx->func = provider->frec->filter_func.out_func;
+ return 1;
+ }
+ }
+
+ /* No provider matched */
+ return 0;
+}
+
+static apr_status_t filter_harness(ap_filter_t *f, apr_bucket_brigade *bb)
+{
+ apr_status_t ret;
+#ifndef NO_PROTOCOL
+ const char *cachecontrol;
+#endif
+ harness_ctx *ctx = f->ctx;
+ ap_filter_rec_t *filter = f->frec;
+
+ if (f->r->status != 200
+ && !apr_table_get(f->r->subprocess_env, "filter-errordocs")) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ filter_trace(f->c, filter->debug, f->frec->name, bb);
+
+ /* look up a handler function if we haven't already set it */
+ if (!ctx->func) {
+#ifndef NO_PROTOCOL
+ if (f->r->proxyreq) {
+ if (filter->proto_flags & AP_FILTER_PROTO_NO_PROXY) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ if (filter->proto_flags & AP_FILTER_PROTO_TRANSFORM) {
+ cachecontrol = apr_table_get(f->r->headers_out,
+ "Cache-Control");
+ if (cachecontrol) {
+ if (ap_strcasestr(cachecontrol, "no-transform")) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+ }
+ }
+ }
+#endif
+ if (!filter_lookup(f, filter)) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+ AP_DEBUG_ASSERT(ctx->func != NULL);
+ }
+
+ /* call the content filter with its own context, then restore our
+ * context
+ */
+ f->ctx = ctx->fctx;
+ ret = ctx->func(f, bb);
+ ctx->fctx = f->ctx;
+ f->ctx = ctx;
+
+ return ret;
+}
+
+#ifndef NO_PROTOCOL
+static const char *filter_protocol(cmd_parms *cmd, void *CFG, const char *fname,
+ const char *pname, const char *proto)
+{
+ static const char *sep = ";, \t";
+ char *arg;
+ char *tok = 0;
+ unsigned int flags = 0;
+ mod_filter_cfg *cfg = CFG;
+ ap_filter_provider_t *provider = NULL;
+ ap_filter_rec_t *filter = apr_hash_get(cfg->live_filters, fname,
+ APR_HASH_KEY_STRING);
+
+ if (!filter) {
+ return "FilterProtocol: No such filter";
+ }
+
+ /* Fixup the args: it's really pname that's optional */
+ if (proto == NULL) {
+ proto = pname;
+ pname = NULL;
+ }
+ else {
+ /* Find provider */
+ for (provider = filter->providers; provider; provider = provider->next) {
+ if (!strcasecmp(provider->frec->name, pname)) {
+ break;
+ }
+ }
+ if (!provider) {
+ return "FilterProtocol: No such provider for this filter";
+ }
+ }
+
+ /* Now set flags from our args */
+ for (arg = apr_strtok(apr_pstrdup(cmd->temp_pool, proto), sep, &tok);
+ arg; arg = apr_strtok(NULL, sep, &tok)) {
+
+ if (!strcasecmp(arg, "change=yes")) {
+ flags |= AP_FILTER_PROTO_CHANGE | AP_FILTER_PROTO_CHANGE_LENGTH;
+ }
+ if (!strcasecmp(arg, "change=no")) {
+ flags &= ~(AP_FILTER_PROTO_CHANGE | AP_FILTER_PROTO_CHANGE_LENGTH);
+ }
+ else if (!strcasecmp(arg, "change=1:1")) {
+ flags |= AP_FILTER_PROTO_CHANGE;
+ }
+ else if (!strcasecmp(arg, "byteranges=no")) {
+ flags |= AP_FILTER_PROTO_NO_BYTERANGE;
+ }
+ else if (!strcasecmp(arg, "proxy=no")) {
+ flags |= AP_FILTER_PROTO_NO_PROXY;
+ }
+ else if (!strcasecmp(arg, "proxy=transform")) {
+ flags |= AP_FILTER_PROTO_TRANSFORM;
+ }
+ else if (!strcasecmp(arg, "cache=no")) {
+ flags |= AP_FILTER_PROTO_NO_CACHE;
+ }
+ }
+
+ if (pname) {
+ provider->frec->proto_flags = flags;
+ }
+ else {
+ filter->proto_flags = flags;
+ }
+
+ return NULL;
+}
+#endif
+
+static const char *filter_declare(cmd_parms *cmd, void *CFG, const char *fname,
+ const char *place)
+{
+ mod_filter_cfg *cfg = (mod_filter_cfg *)CFG;
+ ap_filter_rec_t *filter;
+
+ filter = apr_pcalloc(cmd->pool, sizeof(ap_filter_rec_t));
+ apr_hash_set(cfg->live_filters, fname, APR_HASH_KEY_STRING, filter);
+
+ filter->name = fname;
+ filter->filter_init_func = filter_init;
+ filter->filter_func.out_func = filter_harness;
+ filter->ftype = AP_FTYPE_RESOURCE;
+ filter->next = NULL;
+
+ if (place) {
+ if (!strcasecmp(place, "CONTENT_SET")) {
+ filter->ftype = AP_FTYPE_CONTENT_SET;
+ }
+ else if (!strcasecmp(place, "PROTOCOL")) {
+ filter->ftype = AP_FTYPE_PROTOCOL;
+ }
+ else if (!strcasecmp(place, "CONNECTION")) {
+ filter->ftype = AP_FTYPE_CONNECTION;
+ }
+ else if (!strcasecmp(place, "NETWORK")) {
+ filter->ftype = AP_FTYPE_NETWORK;
+ }
+ }
+
+ return NULL;
+}
+
+static const char *add_filter(cmd_parms *cmd, void *CFG,
+ const char *fname, const char *pname,
+ const char *expr, const char **types)
+{
+ mod_filter_cfg *cfg = CFG;
+ ap_filter_provider_t *provider;
+ const char *c;
+ ap_filter_rec_t* frec;
+ ap_filter_rec_t* provider_frec;
+ ap_expr_info_t *node;
+ const char *err = NULL;
+
+ /* if provider has been registered, we can look it up */
+ provider_frec = ap_get_output_filter_handle(pname);
+ if (!provider_frec) {
+ return apr_psprintf(cmd->pool, "Unknown filter provider %s", pname);
+ }
+
+ /* fname has been declared with DeclareFilter, so we can look it up */
+ frec = apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING);
+
+ /* or if provider is mod_filter itself, we can also look it up */
+ if (!frec) {
+ c = filter_declare(cmd, CFG, fname, NULL);
+ if ( c ) {
+ return c;
+ }
+ frec = apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING);
+ frec->ftype = provider_frec->ftype;
+ }
+
+ if (!frec) {
+ return apr_psprintf(cmd->pool, "Undeclared smart filter %s", fname);
+ }
+
+ provider = apr_palloc(cmd->pool, sizeof(ap_filter_provider_t));
+ if (expr) {
+ node = ap_expr_parse_cmd(cmd, expr, 0, &err, NULL);
+ if (err) {
+ return apr_pstrcat(cmd->pool,
+ "Error parsing FilterProvider expression:", err,
+ NULL);
+ }
+ provider->expr = node;
+ provider->types = NULL;
+ }
+ else {
+ provider->types = types;
+ provider->expr = NULL;
+ }
+ provider->frec = provider_frec;
+ provider->next = frec->providers;
+ frec->providers = provider;
+ return NULL;
+}
+
+static const char *filter_provider(cmd_parms *cmd, void *CFG,
+ const char *fname, const char *pname,
+ const char *expr)
+{
+ return add_filter(cmd, CFG, fname, pname, expr, NULL);
+}
+
+static const char *filter_chain(cmd_parms *cmd, void *CFG, const char *arg)
+{
+ mod_filter_chain *p;
+ mod_filter_chain *q;
+ mod_filter_cfg *cfg = CFG;
+
+ switch (arg[0]) {
+ case '+': /* add to end of chain */
+ p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
+ p->fname = arg+1;
+ if (cfg->chain) {
+ for (q = cfg->chain; q->next; q = q->next);
+ q->next = p;
+ }
+ else {
+ cfg->chain = p;
+ }
+ break;
+
+ case '@': /* add to start of chain */
+ p = apr_palloc(cmd->pool, sizeof(mod_filter_chain));
+ p->fname = arg+1;
+ p->next = cfg->chain;
+ cfg->chain = p;
+ break;
+
+ case '-': /* remove from chain */
+ if (cfg->chain) {
+ if (strcasecmp(cfg->chain->fname, arg+1)) {
+ for (p = cfg->chain; p->next; p = p->next) {
+ if (!strcasecmp(p->next->fname, arg+1)) {
+ p->next = p->next->next;
+ }
+ }
+ }
+ else {
+ cfg->chain = cfg->chain->next;
+ }
+ }
+ break;
+
+ case '!': /* Empty the chain */
+ /** IG: Add a NULL provider to the beginning so that
+ * we can ensure that we'll empty everything before
+ * this when doing config merges later */
+ p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
+ p->fname = NULL;
+ cfg->chain = p;
+ break;
+
+ case '=': /* initialise chain with this arg */
+ /** IG: Prepend a NULL provider to the beginning as above */
+ p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
+ p->fname = NULL;
+ p->next = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
+ p->next->fname = arg+1;
+ cfg->chain = p;
+ break;
+
+ default: /* add to end */
+ p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
+ p->fname = arg;
+ if (cfg->chain) {
+ for (q = cfg->chain; q->next; q = q->next);
+ q->next = p;
+ }
+ else {
+ cfg->chain = p;
+ }
+ break;
+ }
+
+ return NULL;
+}
+
+static const char *filter_bytype1(cmd_parms *cmd, void *CFG,
+ const char *pname, const char **types)
+{
+ const char *rv;
+ const char *fname;
+ int seen_name = 0;
+ mod_filter_cfg *cfg = CFG;
+
+ /* construct fname from name */
+ fname = apr_pstrcat(cmd->pool, "BYTYPE:", pname, NULL);
+
+ /* check whether this is already registered, in which case
+ * it's already in the filter chain
+ */
+ if (apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING)) {
+ seen_name = 1;
+ }
+
+ rv = add_filter(cmd, CFG, fname, pname, NULL, types);
+
+ /* If it's the first time through, add to filterchain */
+ if (rv == NULL && !seen_name) {
+ rv = filter_chain(cmd, CFG, fname);
+ }
+ return rv;
+}
+
+static const char *filter_bytype(cmd_parms *cmd, void *CFG,
+ int argc, char *const argv[])
+{
+ /* back compatibility, need to parse multiple components in filter name */
+ char *pname;
+ char *strtok_state = NULL;
+ char *name;
+ const char **types;
+ const char *rv = NULL;
+ if (argc < 2)
+ return "AddOutputFilterByType requires at least two arguments";
+ name = apr_pstrdup(cmd->temp_pool, argv[0]);
+ types = apr_palloc(cmd->pool, argc * sizeof(char *));
+ memcpy(types, &argv[1], (argc - 1) * sizeof(char *));
+ types[argc-1] = NULL;
+ for (pname = apr_strtok(name, ";", &strtok_state);
+ pname != NULL && rv == NULL;
+ pname = apr_strtok(NULL, ";", &strtok_state)) {
+ rv = filter_bytype1(cmd, CFG, pname, types);
+ }
+ return rv;
+}
+
+static const char *filter_debug(cmd_parms *cmd, void *CFG, const char *fname,
+ const char *level)
+{
+ mod_filter_cfg *cfg = CFG;
+ ap_filter_rec_t *frec = apr_hash_get(cfg->live_filters, fname,
+ APR_HASH_KEY_STRING);
+ if (!frec) {
+ return apr_psprintf(cmd->pool, "Undeclared smart filter %s", fname);
+ }
+ frec->debug = atoi(level);
+
+ return NULL;
+}
+
+static void filter_insert(request_rec *r)
+{
+ mod_filter_chain *p;
+ ap_filter_rec_t *filter;
+ mod_filter_cfg *cfg = ap_get_module_config(r->per_dir_config,
+ &filter_module);
+#ifndef NO_PROTOCOL
+ int ranges = 1;
+ mod_filter_ctx *ctx = apr_pcalloc(r->pool, sizeof(mod_filter_ctx));
+ ap_set_module_config(r->request_config, &filter_module, ctx);
+#endif
+
+ /** IG: Now that we've merged to the final config, go one last time
+ * through the chain, and prune out the NULL filters */
+
+ for (p = cfg->chain; p; p = p->next) {
+ if (p->fname == NULL)
+ cfg->chain = p->next;
+ }
+
+ for (p = cfg->chain; p; p = p->next) {
+ filter = apr_hash_get(cfg->live_filters, p->fname, APR_HASH_KEY_STRING);
+ if (filter == NULL) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01380)
+ "Unknown filter %s not added", p->fname);
+ continue;
+ }
+ ap_add_output_filter_handle(filter, NULL, r, r->connection);
+#ifndef NO_PROTOCOL
+ if (ranges && (filter->proto_flags
+ & (AP_FILTER_PROTO_NO_BYTERANGE
+ | AP_FILTER_PROTO_CHANGE_LENGTH))) {
+ ctx->range = apr_table_get(r->headers_in, "Range");
+ apr_table_unset(r->headers_in, "Range");
+ ranges = 0;
+ }
+#endif
+ }
+}
+
+static void filter_hooks(apr_pool_t *pool)
+{
+ ap_hook_insert_filter(filter_insert, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+static void *filter_config(apr_pool_t *pool, char *x)
+{
+ mod_filter_cfg *cfg = apr_palloc(pool, sizeof(mod_filter_cfg));
+ cfg->live_filters = apr_hash_make(pool);
+ cfg->chain = NULL;
+ return cfg;
+}
+
+static void *filter_merge(apr_pool_t *pool, void *BASE, void *ADD)
+{
+ mod_filter_cfg *base = BASE;
+ mod_filter_cfg *add = ADD;
+ mod_filter_chain *savelink = 0;
+ mod_filter_chain *newlink;
+ mod_filter_chain *p;
+ mod_filter_cfg *conf = apr_palloc(pool, sizeof(mod_filter_cfg));
+
+ conf->live_filters = apr_hash_overlay(pool, add->live_filters,
+ base->live_filters);
+ if (base->chain && add->chain) {
+ for (p = base->chain; p; p = p->next) {
+ newlink = apr_pmemdup(pool, p, sizeof(mod_filter_chain));
+ if (newlink->fname == NULL) {
+ conf->chain = savelink = newlink;
+ }
+ else if (savelink) {
+ savelink->next = newlink;
+ savelink = newlink;
+ }
+ else {
+ conf->chain = savelink = newlink;
+ }
+ }
+
+ for (p = add->chain; p; p = p->next) {
+ newlink = apr_pmemdup(pool, p, sizeof(mod_filter_chain));
+ /** Filter out merged chain resets */
+ if (newlink->fname == NULL) {
+ conf->chain = savelink = newlink;
+ }
+ else if (savelink) {
+ savelink->next = newlink;
+ savelink = newlink;
+ }
+ else {
+ conf->chain = savelink = newlink;
+ }
+ }
+ }
+ else if (add->chain) {
+ conf->chain = add->chain;
+ }
+ else {
+ conf->chain = base->chain;
+ }
+
+ return conf;
+}
+
+static const command_rec filter_cmds[] = {
+ AP_INIT_TAKE12("FilterDeclare", filter_declare, NULL, OR_OPTIONS,
+ "filter-name [filter-type]"),
+ AP_INIT_TAKE3("FilterProvider", filter_provider, NULL, OR_OPTIONS,
+ "filter-name provider-name match-expression"),
+ AP_INIT_ITERATE("FilterChain", filter_chain, NULL, OR_OPTIONS,
+ "list of filter names with optional [+-=!@]"),
+ AP_INIT_TAKE2("FilterTrace", filter_debug, NULL, RSRC_CONF | ACCESS_CONF,
+ "filter-name debug-level"),
+ AP_INIT_TAKE_ARGV("AddOutputFilterByType", filter_bytype, NULL, OR_FILEINFO,
+ "output filter name followed by one or more content-types"),
+#ifndef NO_PROTOCOL
+ AP_INIT_TAKE23("FilterProtocol", filter_protocol, NULL, OR_OPTIONS,
+ "filter-name [provider-name] protocol-args"),
+#endif
+ { NULL }
+};
+
+AP_DECLARE_MODULE(filter) = {
+ STANDARD20_MODULE_STUFF,
+ filter_config,
+ filter_merge,
+ NULL,
+ NULL,
+ filter_cmds,
+ filter_hooks
+};
diff --git a/modules/filters/mod_filter.dep b/modules/filters/mod_filter.dep
new file mode 100644
index 0000000..1701264
--- /dev/null
+++ b/modules/filters/mod_filter.dep
@@ -0,0 +1,50 @@
+# Microsoft Developer Studio Generated Dependency File, included by mod_filter.mak
+
+..\..\build\win32\httpd.rc : \
+ "..\..\include\ap_release.h"\
+
+
+.\mod_filter.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+
diff --git a/modules/filters/mod_filter.dsp b/modules/filters/mod_filter.dsp
new file mode 100644
index 0000000..ee4d484
--- /dev/null
+++ b/modules/filters/mod_filter.dsp
@@ -0,0 +1,111 @@
+# Microsoft Developer Studio Project File - Name="mod_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_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_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_filter.mak" CFG="mod_filter - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_filter - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_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_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 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_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 /fo"Release/mod_filter.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_filter.so" /d LONG_NAME="filter_module for Apache"
+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_filter.so" /base:@..\..\os\win32\BaseAddr.ref,mod_filter.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_filter.so" /base:@..\..\os\win32\BaseAddr.ref,mod_filter.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_filter.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "mod_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_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 /fo"Debug/mod_filter.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_filter.so" /d LONG_NAME="filter_module for Apache"
+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_filter.so" /base:@..\..\os\win32\BaseAddr.ref,mod_filter.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_filter.so" /base:@..\..\os\win32\BaseAddr.ref,mod_filter.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_filter.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_filter - Win32 Release"
+# Name "mod_filter - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\mod_filter.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
diff --git a/modules/filters/mod_filter.mak b/modules/filters/mod_filter.mak
new file mode 100644
index 0000000..c753d9b
--- /dev/null
+++ b/modules/filters/mod_filter.mak
@@ -0,0 +1,353 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on mod_filter.dsp
+!IF "$(CFG)" == ""
+CFG=mod_filter - Win32 Release
+!MESSAGE No configuration specified. Defaulting to mod_filter - Win32 Release.
+!ENDIF
+
+!IF "$(CFG)" != "mod_filter - Win32 Release" && "$(CFG)" != "mod_filter - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!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_filter.mak" CFG="mod_filter - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_filter - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_filter - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(CFG)" == "mod_filter - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_filter.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_filter.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_filter.obj"
+ -@erase "$(INTDIR)\mod_filter.res"
+ -@erase "$(INTDIR)\mod_filter_src.idb"
+ -@erase "$(INTDIR)\mod_filter_src.pdb"
+ -@erase "$(OUTDIR)\mod_filter.exp"
+ -@erase "$(OUTDIR)\mod_filter.lib"
+ -@erase "$(OUTDIR)\mod_filter.pdb"
+ -@erase "$(OUTDIR)\mod_filter.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_filter_src" /FD /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_filter.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_filter.so" /d LONG_NAME="filter_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_filter.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_filter.pdb" /debug /out:"$(OUTDIR)\mod_filter.so" /implib:"$(OUTDIR)\mod_filter.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_filter.so /opt:ref
+LINK32_OBJS= \
+ "$(INTDIR)\mod_filter.obj" \
+ "$(INTDIR)\mod_filter.res" \
+ "..\..\srclib\apr\Release\libapr-1.lib" \
+ "..\..\srclib\apr-util\Release\libaprutil-1.lib" \
+ "..\..\Release\libhttpd.lib"
+
+"$(OUTDIR)\mod_filter.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Release\mod_filter.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_filter.so"
+ if exist .\Release\mod_filter.so.manifest mt.exe -manifest .\Release\mod_filter.so.manifest -outputresource:.\Release\mod_filter.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ELSEIF "$(CFG)" == "mod_filter - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_filter.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_filter.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_filter.obj"
+ -@erase "$(INTDIR)\mod_filter.res"
+ -@erase "$(INTDIR)\mod_filter_src.idb"
+ -@erase "$(INTDIR)\mod_filter_src.pdb"
+ -@erase "$(OUTDIR)\mod_filter.exp"
+ -@erase "$(OUTDIR)\mod_filter.lib"
+ -@erase "$(OUTDIR)\mod_filter.pdb"
+ -@erase "$(OUTDIR)\mod_filter.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_filter_src" /FD /EHsc /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_filter.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_filter.so" /d LONG_NAME="filter_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_filter.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_filter.pdb" /debug /out:"$(OUTDIR)\mod_filter.so" /implib:"$(OUTDIR)\mod_filter.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_filter.so
+LINK32_OBJS= \
+ "$(INTDIR)\mod_filter.obj" \
+ "$(INTDIR)\mod_filter.res" \
+ "..\..\srclib\apr\Debug\libapr-1.lib" \
+ "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \
+ "..\..\Debug\libhttpd.lib"
+
+"$(OUTDIR)\mod_filter.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Debug\mod_filter.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_filter.so"
+ if exist .\Debug\mod_filter.so.manifest mt.exe -manifest .\Debug\mod_filter.so.manifest -outputresource:.\Debug\mod_filter.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+
+!IF "$(NO_EXTERNAL_DEPS)" != "1"
+!IF EXISTS("mod_filter.dep")
+!INCLUDE "mod_filter.dep"
+!ELSE
+!MESSAGE Warning: cannot find "mod_filter.dep"
+!ENDIF
+!ENDIF
+
+
+!IF "$(CFG)" == "mod_filter - Win32 Release" || "$(CFG)" == "mod_filter - Win32 Debug"
+
+!IF "$(CFG)" == "mod_filter - Win32 Release"
+
+"libapr - Win32 Release" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_filter - Win32 Debug"
+
+"libapr - Win32 Debug" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_filter - Win32 Release"
+
+"libaprutil - Win32 Release" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_filter - Win32 Debug"
+
+"libaprutil - Win32 Debug" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_filter - Win32 Release"
+
+"libhttpd - Win32 Release" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 ReleaseCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_filter - Win32 Debug"
+
+"libhttpd - Win32 Debug" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 DebugCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ENDIF
+
+SOURCE=..\..\build\win32\httpd.rc
+
+!IF "$(CFG)" == "mod_filter - Win32 Release"
+
+
+"$(INTDIR)\mod_filter.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_filter.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_filter.so" /d LONG_NAME="filter_module for Apache" $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "mod_filter - Win32 Debug"
+
+
+"$(INTDIR)\mod_filter.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_filter.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_filter.so" /d LONG_NAME="filter_module for Apache" $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\mod_filter.c
+
+"$(INTDIR)\mod_filter.obj" : $(SOURCE) "$(INTDIR)"
+
+
+
+!ENDIF
+
diff --git a/modules/filters/mod_include.c b/modules/filters/mod_include.c
new file mode 100644
index 0000000..584d8fb
--- /dev/null
+++ b/modules/filters/mod_include.c
@@ -0,0 +1,4238 @@
+/* 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.
+ */
+
+#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"
+
+#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"
+#include "mod_include.h"
+#include "ap_expr.h"
+
+/* helper for Latin1 <-> entity encoding */
+#if APR_CHARSET_EBCDIC
+#include "util_ebcdic.h"
+#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 */
+
+
+/*
+ * +-------------------------------------------------------+
+ * | |
+ * | Types and Structures
+ * | |
+ * +-------------------------------------------------------+
+ */
+
+/* sll used for string expansion */
+typedef struct result_item {
+ struct result_item *next;
+ apr_size_t len;
+ const char *string;
+} result_item_t;
+
+/* conditional expression parser stuff */
+typedef enum {
+ 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,
+ TOKEN_ACCESS
+} token_type_t;
+
+typedef struct {
+ token_type_t type;
+ const char *value;
+#ifdef DEBUG_INCLUDE
+ const char *s;
+#endif
+} token_t;
+
+typedef struct parse_node {
+ struct parse_node *parent;
+ struct parse_node *left;
+ struct parse_node *right;
+ token_t token;
+ int value;
+ int done;
+#ifdef DEBUG_INCLUDE
+ int dump_done;
+#endif
+} parse_node_t;
+
+typedef enum {
+ XBITHACK_OFF,
+ XBITHACK_ON,
+ XBITHACK_FULL,
+ XBITHACK_UNSET
+} xbithack_t;
+
+typedef struct {
+ const char *default_error_msg;
+ const char *default_time_fmt;
+ const char *undefined_echo;
+ xbithack_t xbithack;
+ signed char lastmodified;
+ signed char etag;
+ signed char legacy_expr;
+} include_dir_config;
+
+typedef struct {
+ const char *default_start_tag;
+ const char *default_end_tag;
+} 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 arg_item {
+ struct arg_item *next;
+ char *name;
+ apr_size_t name_len;
+ char *value;
+ apr_size_t value_len;
+} arg_item_t;
+
+typedef struct {
+ const char *source;
+ const char *rexp;
+ apr_size_t nsub;
+ ap_regmatch_t match[AP_MAX_REG_MATCH];
+ int have_match;
+} backref_t;
+
+typedef struct {
+ unsigned int T[256];
+ unsigned int x;
+ apr_size_t pattern_len;
+} bndm_t;
+
+struct ssi_internal_ctx {
+ parse_state_t state;
+ int seen_eos;
+ int error;
+ char quote; /* quote character value (or \0) */
+ apr_size_t parse_pos; /* parse position of partial matches */
+ apr_size_t bytes_read;
+
+ apr_bucket_brigade *tmp_bb;
+
+ const char *start_seq;
+ bndm_t *start_seq_pat;
+ const char *end_seq;
+ apr_size_t end_seq_len;
+ char *directive; /* name of the current directive */
+ apr_size_t directive_len; /* length of the current directive name */
+
+ arg_item_t *current_arg; /* currently parsed argument */
+ arg_item_t *argv; /* all arguments */
+
+ backref_t *re; /* NULL if there wasn't a regex yet */
+
+ const char *undefined_echo;
+ apr_size_t undefined_echo_len;
+
+ char legacy_expr; /* use ap_expr or legacy mod_include
+ expression parser? */
+
+ ap_expr_eval_ctx_t *expr_eval_ctx; /* NULL if there wasn't an ap_expr yet */
+ const char *expr_vary_this; /* for use by ap_expr_eval_ctx */
+ const char *expr_err; /* for use by ap_expr_eval_ctx */
+#ifdef DEBUG_INCLUDE
+ struct {
+ ap_filter_t *f;
+ apr_bucket_brigade *bb;
+ } debug;
+#endif
+};
+
+
+/*
+ * +-------------------------------------------------------+
+ * | |
+ * | Debugging Utilities
+ * | |
+ * +-------------------------------------------------------+
+ */
+
+#ifdef DEBUG_INCLUDE
+
+#define TYPE_TOKEN(token, ttype) do { \
+ (token)->type = ttype; \
+ (token)->s = #ttype; \
+} while(0)
+
+#define CREATE_NODE(ctx, name) do { \
+ (name) = apr_palloc((ctx)->dpool, sizeof(*(name))); \
+ (name)->parent = (name)->left = (name)->right = NULL; \
+ (name)->done = 0; \
+ (name)->dump_done = 0; \
+} while(0)
+
+static void debug_printf(include_ctx_t *ctx, const char *fmt, ...)
+{
+ va_list ap;
+ char *debug__str;
+
+ va_start(ap, fmt);
+ debug__str = apr_pvsprintf(ctx->pool, fmt, ap);
+ va_end(ap);
+
+ APR_BRIGADE_INSERT_TAIL(ctx->intern->debug.bb, apr_bucket_pool_create(
+ debug__str, strlen(debug__str), ctx->pool,
+ ctx->intern->debug.f->c->bucket_alloc));
+}
+
+#define DUMP__CHILD(ctx, is, node, child) if (1) { \
+ parse_node_t *d__c = node->child; \
+ if (d__c) { \
+ if (!d__c->dump_done) { \
+ if (d__c->parent != node) { \
+ debug_printf(ctx, "!!! Parse tree is not consistent !!!\n"); \
+ if (!d__c->parent) { \
+ debug_printf(ctx, "Parent of " #child " child node is " \
+ "NULL.\n"); \
+ } \
+ else { \
+ debug_printf(ctx, "Parent of " #child " child node " \
+ "points to another node (of type %s)!\n", \
+ d__c->parent->token.s); \
+ } \
+ return; \
+ } \
+ node = d__c; \
+ continue; \
+ } \
+ } \
+ else { \
+ debug_printf(ctx, "%s(missing)\n", is); \
+ } \
+}
+
+static void debug_dump_tree(include_ctx_t *ctx, parse_node_t *root)
+{
+ parse_node_t *current;
+ char *is;
+
+ if (!root) {
+ debug_printf(ctx, " -- Parse Tree empty --\n\n");
+ return;
+ }
+
+ debug_printf(ctx, " ----- Parse Tree -----\n");
+ current = root;
+ is = " ";
+
+ while (current) {
+ switch (current->token.type) {
+ case TOKEN_STRING:
+ case TOKEN_RE:
+ debug_printf(ctx, "%s%s (%s)\n", is, current->token.s,
+ current->token.value);
+ current->dump_done = 1;
+ current = current->parent;
+ continue;
+
+ case TOKEN_NOT:
+ case TOKEN_GROUP:
+ case TOKEN_RBRACE:
+ case TOKEN_LBRACE:
+ if (!current->dump_done) {
+ debug_printf(ctx, "%s%s\n", is, current->token.s);
+ is = apr_pstrcat(ctx->dpool, is, " ", NULL);
+ current->dump_done = 1;
+ }
+
+ DUMP__CHILD(ctx, is, current, right)
+
+ if (!current->right || current->right->dump_done) {
+ is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
+ if (current->right) current->right->dump_done = 0;
+ current = current->parent;
+ }
+ continue;
+
+ default:
+ if (!current->dump_done) {
+ debug_printf(ctx, "%s%s\n", is, current->token.s);
+ is = apr_pstrcat(ctx->dpool, is, " ", NULL);
+ current->dump_done = 1;
+ }
+
+ DUMP__CHILD(ctx, is, current, left)
+ DUMP__CHILD(ctx, is, current, right)
+
+ if ((!current->left || current->left->dump_done) &&
+ (!current->right || current->right->dump_done)) {
+
+ is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
+ if (current->left) current->left->dump_done = 0;
+ if (current->right) current->right->dump_done = 0;
+ current = current->parent;
+ }
+ continue;
+ }
+ }
+
+ /* it is possible to call this function within the parser loop, to see
+ * how the tree is built. That way, we must cleanup after us to dump
+ * always the whole tree
+ */
+ root->dump_done = 0;
+ if (root->left) root->left->dump_done = 0;
+ if (root->right) root->right->dump_done = 0;
+
+ debug_printf(ctx, " --- End Parse Tree ---\n\n");
+}
+
+#define DEBUG_INIT(ctx, filter, brigade) do { \
+ (ctx)->intern->debug.f = filter; \
+ (ctx)->intern->debug.bb = brigade; \
+} while(0)
+
+#define DEBUG_PRINTF(arg) debug_printf arg
+
+#define DEBUG_DUMP_TOKEN(ctx, token) do { \
+ token_t *d__t = (token); \
+ \
+ if (d__t->type == TOKEN_STRING || d__t->type == TOKEN_RE) { \
+ DEBUG_PRINTF(((ctx), " Found: %s (%s)\n", d__t->s, d__t->value)); \
+ } \
+ else { \
+ DEBUG_PRINTF((ctx, " Found: %s\n", d__t->s)); \
+ } \
+} while(0)
+
+#define DEBUG_DUMP_EVAL(ctx, node) do { \
+ char c = '"'; \
+ switch ((node)->token.type) { \
+ case TOKEN_STRING: \
+ debug_printf((ctx), " Evaluate: %s (%s) -> %c\n", (node)->token.s,\
+ (node)->token.value, ((node)->value) ? '1':'0'); \
+ break; \
+ case TOKEN_AND: \
+ case TOKEN_OR: \
+ debug_printf((ctx), " Evaluate: %s (Left: %s; Right: %s) -> %c\n",\
+ (node)->token.s, \
+ (((node)->left->done) ? ((node)->left->value ?"1":"0") \
+ : "short circuited"), \
+ (((node)->right->done) ? ((node)->right->value?"1":"0") \
+ : "short circuited"), \
+ (node)->value ? '1' : '0'); \
+ break; \
+ case TOKEN_EQ: \
+ case TOKEN_NE: \
+ case TOKEN_GT: \
+ case TOKEN_GE: \
+ case TOKEN_LT: \
+ case TOKEN_LE: \
+ if ((node)->right->token.type == TOKEN_RE) c = '/'; \
+ debug_printf((ctx), " Compare: %s (\"%s\" with %c%s%c) -> %c\n", \
+ (node)->token.s, \
+ (node)->left->token.value, \
+ c, (node)->right->token.value, c, \
+ (node)->value ? '1' : '0'); \
+ break; \
+ default: \
+ debug_printf((ctx), " Evaluate: %s -> %c\n", (node)->token.s, \
+ (node)->value ? '1' : '0'); \
+ break; \
+ } \
+} while(0)
+
+#define DEBUG_DUMP_UNMATCHED(ctx, unmatched) do { \
+ if (unmatched) { \
+ DEBUG_PRINTF(((ctx), " Unmatched %c\n", (char)(unmatched))); \
+ } \
+} while(0)
+
+#define DEBUG_DUMP_COND(ctx, text) \
+ DEBUG_PRINTF(((ctx), "**** %s cond status=\"%c\"\n", (text), \
+ ((ctx)->flags & SSI_FLAG_COND_TRUE) ? '1' : '0'))
+
+#define DEBUG_DUMP_TREE(ctx, root) debug_dump_tree(ctx, root)
+
+#else /* DEBUG_INCLUDE */
+
+#define TYPE_TOKEN(token, ttype) (token)->type = ttype
+
+#define CREATE_NODE(ctx, name) do { \
+ (name) = apr_palloc((ctx)->dpool, sizeof(*(name))); \
+ (name)->parent = (name)->left = (name)->right = NULL; \
+ (name)->done = 0; \
+} while(0)
+
+#define DEBUG_INIT(ctx, f, bb)
+#define DEBUG_PRINTF(arg)
+#define DEBUG_DUMP_TOKEN(ctx, token)
+#define DEBUG_DUMP_EVAL(ctx, node)
+#define DEBUG_DUMP_UNMATCHED(ctx, unmatched)
+#define DEBUG_DUMP_COND(ctx, text)
+#define DEBUG_DUMP_TREE(ctx, root)
+
+#endif /* !DEBUG_INCLUDE */
+
+
+/*
+ * +-------------------------------------------------------+
+ * | |
+ * | Static Module Data
+ * | |
+ * +-------------------------------------------------------+
+ */
+
+/* global module structure */
+module AP_MODULE_DECLARE_DATA include_module;
+
+/* function handlers for include directives */
+static apr_hash_t *include_handlers;
+
+/* forward declaration of handler registry */
+static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *ssi_pfn_register;
+
+/* 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 = '\0';
+#define LAZY_VALUE (&lazy_eval_sentinel)
+
+/* default values */
+#define DEFAULT_START_SEQUENCE "<!--#"
+#define DEFAULT_END_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 DEFAULT_UNDEFINED_ECHO "(none)"
+
+#define UNSET -1
+
+#ifdef XBITHACK
+#define DEFAULT_XBITHACK XBITHACK_FULL
+#else
+#define DEFAULT_XBITHACK XBITHACK_OFF
+#endif
+
+
+/*
+ * +-------------------------------------------------------+
+ * | |
+ * | Environment/Expansion Functions
+ * | |
+ * +-------------------------------------------------------+
+ */
+
+/*
+ * 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, &#00; will not be decoded, but will be deleted.
+ */
+
+/* maximum length of any ISO-LATIN-1 HTML entity name. */
+#define MAXENTLEN (6)
+
+/* The following is a shrinking transformation, therefore safe. */
+
+/* Note: this function is deprecated in favour of apr_unescape_entity() in APR */
+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"
+ "\353iuml\357ouml\366uuml\374yuml\377", /* 4 */
+
+ "Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc"
+ "\333THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352"
+ "icirc\356ocirc\364ucirc\373thorn\376", /* 5 */
+
+ "Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311"
+ "Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde"
+ "\325Oslash\330Ugrave\331Uacute\332Yacute\335agrave\340"
+ "aacute\341atilde\343ccedil\347egrave\350eacute\351igrave"
+ "\354iacute\355ntilde\361ograve\362oacute\363otilde\365"
+ "oslash\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';
+}
+
+static void add_include_vars(request_rec *r)
+{
+ 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);
+ apr_table_setn(e, "DOCUMENT_ARGS", r->args ? r->args : "");
+ 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, const char *timefmt)
+{
+ char *val;
+ if (!strcasecmp(var, "DATE_LOCAL")) {
+ val = ap_ht_time(r->pool, r->request_time, timefmt, 0);
+ }
+ else if (!strcasecmp(var, "DATE_GMT")) {
+ val = ap_ht_time(r->pool, r->request_time, timefmt, 1);
+ }
+ else if (!strcasecmp(var, "LAST_MODIFIED")) {
+ val = ap_ht_time(r->pool, r->finfo.mtime, timefmt, 0);
+ }
+ else if (!strcasecmp(var, "USER_NAME")) {
+ if (apr_uid_name_get(&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(const char *var, include_ctx_t *ctx)
+{
+ const char *val;
+ request_rec *r = ctx->r;
+
+ if (apr_isdigit(*var) && !var[1]) {
+ apr_size_t idx = *var - '0';
+ backref_t *re = ctx->intern->re;
+
+ /* 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 (!re || !re->have_match) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01329)
+ "regex capture $%" APR_SIZE_T_FMT " refers to no regex in %s",
+ idx, r->filename);
+ return NULL;
+ }
+ else if (re->nsub < idx || idx >= AP_MAX_REG_MATCH) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01330)
+ "regex capture $%" APR_SIZE_T_FMT
+ " is out of range (last regex was: '%s') in %s",
+ idx, re->rexp, r->filename);
+ return NULL;
+ }
+ else if (re->match[idx].rm_so < 0 || re->match[idx].rm_eo < 0) {
+ /* This particular subpattern was not used by the regex */
+ return NULL;
+ }
+ else {
+ val = apr_pstrmemdup(ctx->dpool, re->source + re->match[idx].rm_so,
+ re->match[idx].rm_eo - re->match[idx].rm_so);
+ }
+ }
+ else {
+ val = apr_table_get(r->subprocess_env, var);
+
+ if (val == LAZY_VALUE) {
+ val = add_include_vars_lazy(r, var, ctx->time_str);
+ }
+ }
+
+ return val;
+}
+
+static const char *include_expr_var_fn(ap_expr_eval_ctx_t *eval_ctx,
+ const void *data,
+ const char *arg)
+{
+ const char *res, *name = data;
+ include_ctx_t *ctx = eval_ctx->data;
+ if ((name[0] == 'e') || (name[0] == 'E')) {
+ /* keep legacy "env" semantics */
+ if ((res = apr_table_get(ctx->r->notes, arg)) != NULL)
+ return res;
+ else if ((res = get_include_var(arg, ctx)) != NULL)
+ return res;
+ else
+ return getenv(arg);
+ }
+ else {
+ return get_include_var(arg, ctx);
+ }
+}
+
+static int include_expr_lookup(ap_expr_lookup_parms *parms)
+{
+ switch (parms->type) {
+ case AP_EXPR_FUNC_STRING:
+ if (strcasecmp(parms->name, "v") == 0 ||
+ strcasecmp(parms->name, "reqenv") == 0 ||
+ strcasecmp(parms->name, "env") == 0) {
+ *parms->func = include_expr_var_fn;
+ *parms->data = parms->name;
+ return OK;
+ }
+ break;
+ /*
+ * We could also make the SSI vars available as %{...} style variables
+ * (AP_EXPR_FUNC_VAR), but this would create problems if we ever want
+ * to cache parsed expressions for performance reasons.
+ */
+ }
+ return ap_run_expr_lookup(parms);
+}
+
+
+/*
+ * Do variable substitution on strings
+ *
+ * (Note: If out==NULL, this function allocs a buffer for the resulting
+ * string from ctx->pool. The return value is always the parsed string)
+ */
+static char *ap_ssi_parse_string(include_ctx_t *ctx, const char *in, char *out,
+ apr_size_t length, int leave_name)
+{
+ request_rec *r = ctx->r;
+ result_item_t *result = NULL, *current = NULL;
+ apr_size_t outlen = 0, inlen, span;
+ char *ret = NULL, *eout = NULL;
+ const char *p;
+
+ if (out) {
+ /* sanity check, out && !length is not supported */
+ ap_assert(out && length);
+
+ ret = out;
+ eout = out + length - 1;
+ }
+
+ span = strcspn(in, "\\$");
+ inlen = strlen(in);
+
+ /* fast exit */
+ if (inlen == span) {
+ if (out) {
+ apr_cpystrn(out, in, length);
+ }
+ else {
+ ret = apr_pstrmemdup(ctx->pool, in, (length && length <= inlen)
+ ? length - 1 : inlen);
+ }
+
+ return ret;
+ }
+
+ /* well, actually something to do */
+ p = in + span;
+
+ if (out) {
+ if (span) {
+ memcpy(out, in, (out+span <= eout) ? span : (eout-out));
+ out += span;
+ }
+ }
+ else {
+ current = result = apr_palloc(ctx->dpool, sizeof(*result));
+ current->next = NULL;
+ current->string = in;
+ current->len = span;
+ outlen = span;
+ }
+
+ /* loop for specials */
+ do {
+ if ((out && out >= eout) || (length && outlen >= length)) {
+ break;
+ }
+
+ /* prepare next entry */
+ if (!out && current->len) {
+ current->next = apr_palloc(ctx->dpool, sizeof(*current->next));
+ current = current->next;
+ current->next = NULL;
+ current->len = 0;
+ }
+
+ /*
+ * escaped character
+ */
+ if (*p == '\\') {
+ if (out) {
+ *out++ = (p[1] == '$') ? *++p : *p;
+ ++p;
+ }
+ else {
+ current->len = 1;
+ current->string = (p[1] == '$') ? ++p : p;
+ ++p;
+ ++outlen;
+ }
+ }
+
+ /*
+ * variable expansion
+ */
+ else { /* *p == '$' */
+ const char *newp = NULL, *ep, *key = NULL;
+
+ if (*++p == '{') {
+ ep = ap_strchr_c(++p, '}');
+ if (!ep) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01331) "Missing '}' on "
+ "variable \"%s\" in %s", p, r->filename);
+ break;
+ }
+
+ if (p < ep) {
+ key = apr_pstrmemdup(ctx->dpool, p, ep - p);
+ newp = ep + 1;
+ }
+ p -= 2;
+ }
+ else {
+ ep = p;
+ while (*ep == '_' || apr_isalnum(*ep)) {
+ ++ep;
+ }
+
+ if (p < ep) {
+ key = apr_pstrmemdup(ctx->dpool, p, ep - p);
+ newp = ep;
+ }
+ --p;
+ }
+
+ /* empty name results in a copy of '$' in the output string */
+ if (!key) {
+ if (out) {
+ *out++ = *p++;
+ }
+ else {
+ current->len = 1;
+ current->string = p++;
+ ++outlen;
+ }
+ }
+ else {
+ const char *val = get_include_var(key, ctx);
+ apr_size_t len = 0;
+
+ if (val) {
+ len = strlen(val);
+ }
+ else if (leave_name) {
+ val = p;
+ len = ep - p;
+ }
+
+ if (val && len) {
+ if (out) {
+ memcpy(out, val, (out+len <= eout) ? len : (eout-out));
+ out += len;
+ }
+ else {
+ current->len = len;
+ current->string = val;
+ outlen += len;
+ }
+ }
+
+ p = newp;
+ }
+ }
+
+ if ((out && out >= eout) || (length && outlen >= length)) {
+ break;
+ }
+
+ /* check the remainder */
+ if (*p && (span = strcspn(p, "\\$")) > 0) {
+ if (!out && current->len) {
+ current->next = apr_palloc(ctx->dpool, sizeof(*current->next));
+ current = current->next;
+ current->next = NULL;
+ }
+
+ if (out) {
+ memcpy(out, p, (out+span <= eout) ? span : (eout-out));
+ out += span;
+ }
+ else {
+ current->len = span;
+ current->string = p;
+ outlen += span;
+ }
+
+ p += span;
+ }
+ } while (p < in+inlen);
+
+ /* assemble result */
+ if (out) {
+ if (out > eout) {
+ *eout = '\0';
+ }
+ else {
+ *out = '\0';
+ }
+ }
+ else {
+ const char *ep;
+
+ if (length && outlen > length) {
+ outlen = length - 1;
+ }
+
+ ret = out = apr_palloc(ctx->pool, outlen + 1);
+ ep = ret + outlen;
+
+ do {
+ if (result->len) {
+ memcpy(out, result->string, (out+result->len <= ep)
+ ? result->len : (ep-out));
+ out += result->len;
+ }
+ result = result->next;
+ } while (result && out < ep);
+
+ ret[outlen] = '\0';
+ }
+
+ return ret;
+}
+
+
+/*
+ * +-------------------------------------------------------+
+ * | |
+ * | Conditional Expression Parser
+ * | |
+ * +-------------------------------------------------------+
+ */
+
+static APR_INLINE int re_check(include_ctx_t *ctx, const char *string,
+ const char *rexp)
+{
+ ap_regex_t *compiled;
+ backref_t *re = ctx->intern->re;
+
+ compiled = ap_pregcomp(ctx->dpool, rexp, AP_REG_EXTENDED);
+ if (!compiled) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(02667)
+ "unable to compile pattern \"%s\"", rexp);
+ return -1;
+ }
+
+ if (!re) {
+ re = ctx->intern->re = apr_palloc(ctx->pool, sizeof(*re));
+ }
+
+ re->source = apr_pstrdup(ctx->pool, string);
+ re->rexp = apr_pstrdup(ctx->pool, rexp);
+ re->nsub = compiled->re_nsub;
+ re->have_match = !ap_regexec(compiled, string, AP_MAX_REG_MATCH,
+ re->match, 0);
+
+ ap_pregfree(ctx->dpool, compiled);
+ return re->have_match;
+}
+
+static int get_ptoken(include_ctx_t *ctx, const char **parse, token_t *token, token_t *previous)
+{
+ const char *p;
+ apr_size_t shift;
+ int unmatched;
+
+ token->value = NULL;
+
+ if (!*parse) {
+ return 0;
+ }
+
+ /* Skip leading white space */
+ while (apr_isspace(**parse)) {
+ ++*parse;
+ }
+
+ if (!**parse) {
+ *parse = NULL;
+ return 0;
+ }
+
+ TYPE_TOKEN(token, TOKEN_STRING); /* the default type */
+ p = *parse;
+ unmatched = 0;
+
+ switch (*(*parse)++) {
+ case '(':
+ TYPE_TOKEN(token, TOKEN_LBRACE);
+ return 0;
+ case ')':
+ TYPE_TOKEN(token, TOKEN_RBRACE);
+ return 0;
+ case '=':
+ if (**parse == '=') ++*parse;
+ TYPE_TOKEN(token, TOKEN_EQ);
+ return 0;
+ case '!':
+ if (**parse == '=') {
+ TYPE_TOKEN(token, TOKEN_NE);
+ ++*parse;
+ return 0;
+ }
+ TYPE_TOKEN(token, TOKEN_NOT);
+ return 0;
+ case '\'':
+ unmatched = '\'';
+ break;
+ case '/':
+ /* if last token was ACCESS, this token is STRING */
+ if (previous != NULL && TOKEN_ACCESS == previous->type) {
+ break;
+ }
+ TYPE_TOKEN(token, TOKEN_RE);
+ unmatched = '/';
+ break;
+ case '|':
+ if (**parse == '|') {
+ TYPE_TOKEN(token, TOKEN_OR);
+ ++*parse;
+ return 0;
+ }
+ break;
+ case '&':
+ if (**parse == '&') {
+ TYPE_TOKEN(token, TOKEN_AND);
+ ++*parse;
+ return 0;
+ }
+ break;
+ case '>':
+ if (**parse == '=') {
+ TYPE_TOKEN(token, TOKEN_GE);
+ ++*parse;
+ return 0;
+ }
+ TYPE_TOKEN(token, TOKEN_GT);
+ return 0;
+ case '<':
+ if (**parse == '=') {
+ TYPE_TOKEN(token, TOKEN_LE);
+ ++*parse;
+ return 0;
+ }
+ TYPE_TOKEN(token, TOKEN_LT);
+ return 0;
+ case '-':
+ if (**parse == 'A') {
+ TYPE_TOKEN(token, TOKEN_ACCESS);
+ ++*parse;
+ return 0;
+ }
+ break;
+ }
+
+ /* It's a string or regex token
+ * Now search for the next token, which finishes this string
+ */
+ shift = 0;
+ p = *parse = token->value = unmatched ? *parse : p;
+
+ for (; **parse; p = ++*parse) {
+ if (**parse == '\\') {
+ if (!*(++*parse)) {
+ p = *parse;
+ break;
+ }
+
+ ++shift;
+ }
+ else {
+ if (unmatched) {
+ if (**parse == unmatched) {
+ unmatched = 0;
+ ++*parse;
+ break;
+ }
+ } else if (apr_isspace(**parse)) {
+ break;
+ }
+ else {
+ int found = 0;
+
+ switch (**parse) {
+ case '(':
+ case ')':
+ case '=':
+ case '!':
+ case '<':
+ case '>':
+ ++found;
+ break;
+
+ case '|':
+ case '&':
+ if ((*parse)[1] == **parse) {
+ ++found;
+ }
+ break;
+ }
+
+ if (found) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (unmatched) {
+ token->value = apr_pstrdup(ctx->dpool, "");
+ }
+ else {
+ apr_size_t len = p - token->value - shift;
+ char *c = apr_palloc(ctx->dpool, len + 1);
+
+ p = token->value;
+ token->value = c;
+
+ while (shift--) {
+ const char *e = ap_strchr_c(p, '\\');
+
+ memcpy(c, p, e-p);
+ c += e-p;
+ *c++ = *++e;
+ len -= e-p;
+ p = e+1;
+ }
+
+ if (len) {
+ memcpy(c, p, len);
+ }
+ c[len] = '\0';
+ }
+
+ return unmatched;
+}
+
+static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error)
+{
+ parse_node_t *new, *root = NULL, *current = NULL;
+ request_rec *r = ctx->r;
+ request_rec *rr = NULL;
+ const char *error = APLOGNO(03188) "Invalid expression \"%s\" in file %s";
+ const char *parse = expr;
+ unsigned regex = 0;
+
+ *was_error = 0;
+
+ if (!parse) {
+ return 0;
+ }
+
+ /* Create Parse Tree */
+ while (1) {
+ /* uncomment this to see how the tree a built:
+ *
+ * DEBUG_DUMP_TREE(ctx, root);
+ */
+ CREATE_NODE(ctx, new);
+
+ {
+#ifdef DEBUG_INCLUDE
+ int was_unmatched =
+#endif
+ get_ptoken(ctx, &parse, &new->token,
+ (current != NULL ? &current->token : NULL));
+ if (!parse)
+ break;
+
+ DEBUG_DUMP_UNMATCHED(ctx, was_unmatched);
+ DEBUG_DUMP_TOKEN(ctx, &new->token);
+ }
+
+ if (!current) {
+ switch (new->token.type) {
+ case TOKEN_STRING:
+ case TOKEN_NOT:
+ case TOKEN_ACCESS:
+ case TOKEN_LBRACE:
+ root = current = new;
+ continue;
+
+ default:
+ /* Intentional no APLOGNO */
+ /* error text provides APLOGNO */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, expr,
+ r->filename);
+ *was_error = 1;
+ return 0;
+ }
+ }
+
+ switch (new->token.type) {
+ case TOKEN_STRING:
+ switch (current->token.type) {
+ case TOKEN_STRING:
+ current->token.value =
+ apr_pstrcat(ctx->dpool, current->token.value,
+ *current->token.value ? " " : "",
+ new->token.value, NULL);
+ continue;
+
+ case TOKEN_RE:
+ case TOKEN_RBRACE:
+ case TOKEN_GROUP:
+ break;
+
+ default:
+ new->parent = current;
+ current = current->right = new;
+ continue;
+ }
+ break;
+
+ case TOKEN_RE:
+ switch (current->token.type) {
+ case TOKEN_EQ:
+ case TOKEN_NE:
+ new->parent = current;
+ current = current->right = new;
+ ++regex;
+ continue;
+
+ default:
+ break;
+ }
+ break;
+
+ case TOKEN_AND:
+ case TOKEN_OR:
+ switch (current->token.type) {
+ case TOKEN_STRING:
+ case TOKEN_RE:
+ case TOKEN_GROUP:
+ current = current->parent;
+
+ while (current) {
+ switch (current->token.type) {
+ case TOKEN_AND:
+ case TOKEN_OR:
+ case TOKEN_LBRACE:
+ break;
+
+ default:
+ current = current->parent;
+ continue;
+ }
+ break;
+ }
+
+ if (!current) {
+ new->left = root;
+ root->parent = new;
+ current = root = new;
+ continue;
+ }
+
+ new->left = current->right;
+ new->left->parent = new;
+ new->parent = current;
+ current = current->right = new;
+ continue;
+
+ default:
+ break;
+ }
+ break;
+
+ case TOKEN_EQ:
+ case TOKEN_NE:
+ case TOKEN_GE:
+ case TOKEN_GT:
+ case TOKEN_LE:
+ case TOKEN_LT:
+ if (current->token.type == TOKEN_STRING) {
+ current = current->parent;
+
+ if (!current) {
+ new->left = root;
+ root->parent = new;
+ current = root = new;
+ continue;
+ }
+
+ switch (current->token.type) {
+ case TOKEN_LBRACE:
+ case TOKEN_AND:
+ case TOKEN_OR:
+ new->left = current->right;
+ new->left->parent = new;
+ new->parent = current;
+ current = current->right = new;
+ continue;
+
+ default:
+ break;
+ }
+ }
+ break;
+
+ case TOKEN_RBRACE:
+ while (current && current->token.type != TOKEN_LBRACE) {
+ current = current->parent;
+ }
+
+ if (current) {
+ TYPE_TOKEN(&current->token, TOKEN_GROUP);
+ continue;
+ }
+
+ error = APLOGNO(03189) "Unmatched ')' in \"%s\" in file %s";
+ break;
+
+ case TOKEN_NOT:
+ case TOKEN_ACCESS:
+ case TOKEN_LBRACE:
+ switch (current->token.type) {
+ case TOKEN_STRING:
+ case TOKEN_RE:
+ case TOKEN_RBRACE:
+ case TOKEN_GROUP:
+ break;
+
+ default:
+ current->right = new;
+ new->parent = current;
+ current = new;
+ continue;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /* Intentional no APLOGNO */
+ /* error text provides APLOGNO */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, expr, r->filename);
+ *was_error = 1;
+ return 0;
+ }
+
+ DEBUG_DUMP_TREE(ctx, root);
+
+ /* Evaluate Parse Tree */
+ current = root;
+ error = NULL;
+ while (current) {
+ switch (current->token.type) {
+ case TOKEN_STRING:
+ current->token.value =
+ ap_ssi_parse_string(ctx, current->token.value, NULL, 0,
+ SSI_EXPAND_DROP_NAME);
+ current->value = !!*current->token.value;
+ break;
+
+ case TOKEN_AND:
+ case TOKEN_OR:
+ if (!current->left || !current->right) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01332)
+ "Invalid expression \"%s\" in file %s",
+ expr, r->filename);
+ *was_error = 1;
+ return 0;
+ }
+
+ if (!current->left->done) {
+ switch (current->left->token.type) {
+ case TOKEN_STRING:
+ current->left->token.value =
+ ap_ssi_parse_string(ctx, current->left->token.value,
+ NULL, 0, SSI_EXPAND_DROP_NAME);
+ current->left->value = !!*current->left->token.value;
+ DEBUG_DUMP_EVAL(ctx, current->left);
+ current->left->done = 1;
+ break;
+
+ default:
+ current = current->left;
+ continue;
+ }
+ }
+
+ /* short circuit evaluation */
+ if (!current->right->done && !regex &&
+ ((current->token.type == TOKEN_AND && !current->left->value) ||
+ (current->token.type == TOKEN_OR && current->left->value))) {
+ current->value = current->left->value;
+ }
+ else {
+ if (!current->right->done) {
+ switch (current->right->token.type) {
+ case TOKEN_STRING:
+ current->right->token.value =
+ ap_ssi_parse_string(ctx,current->right->token.value,
+ NULL, 0, SSI_EXPAND_DROP_NAME);
+ current->right->value = !!*current->right->token.value;
+ DEBUG_DUMP_EVAL(ctx, current->right);
+ current->right->done = 1;
+ break;
+
+ default:
+ current = current->right;
+ continue;
+ }
+ }
+
+ if (current->token.type == TOKEN_AND) {
+ current->value = current->left->value &&
+ current->right->value;
+ }
+ else {
+ current->value = current->left->value ||
+ current->right->value;
+ }
+ }
+ break;
+
+ case TOKEN_EQ:
+ case TOKEN_NE:
+ if (!current->left || !current->right ||
+ 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, APLOGNO(01333)
+ "Invalid expression \"%s\" in file %s",
+ expr, r->filename);
+ *was_error = 1;
+ return 0;
+ }
+ current->left->token.value =
+ ap_ssi_parse_string(ctx, current->left->token.value, NULL, 0,
+ SSI_EXPAND_DROP_NAME);
+ current->right->token.value =
+ ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
+ SSI_EXPAND_DROP_NAME);
+
+ if (current->right->token.type == TOKEN_RE) {
+ current->value = re_check(ctx, current->left->token.value,
+ current->right->token.value);
+ --regex;
+ }
+ else {
+ current->value = !strcmp(current->left->token.value,
+ current->right->token.value);
+ }
+
+ if (current->token.type == TOKEN_NE) {
+ current->value = !current->value;
+ }
+ break;
+
+ case TOKEN_GE:
+ case TOKEN_GT:
+ case TOKEN_LE:
+ case TOKEN_LT:
+ if (!current->left || !current->right ||
+ current->left->token.type != TOKEN_STRING ||
+ current->right->token.type != TOKEN_STRING) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01334)
+ "Invalid expression \"%s\" in file %s",
+ expr, r->filename);
+ *was_error = 1;
+ return 0;
+ }
+
+ current->left->token.value =
+ ap_ssi_parse_string(ctx, current->left->token.value, NULL, 0,
+ SSI_EXPAND_DROP_NAME);
+ current->right->token.value =
+ ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
+ SSI_EXPAND_DROP_NAME);
+
+ current->value = strcmp(current->left->token.value,
+ current->right->token.value);
+
+ switch (current->token.type) {
+ case TOKEN_GE: current->value = current->value >= 0; break;
+ case TOKEN_GT: current->value = current->value > 0; break;
+ case TOKEN_LE: current->value = current->value <= 0; break;
+ case TOKEN_LT: current->value = current->value < 0; break;
+ default: current->value = 0; break; /* should not happen */
+ }
+ break;
+
+ case TOKEN_NOT:
+ case TOKEN_GROUP:
+ if (current->right) {
+ if (!current->right->done) {
+ current = current->right;
+ continue;
+ }
+ current->value = current->right->value;
+ }
+ else {
+ current->value = 1;
+ }
+
+ if (current->token.type == TOKEN_NOT) {
+ current->value = !current->value;
+ }
+ break;
+
+ case TOKEN_ACCESS:
+ if (current->left || !current->right ||
+ (current->right->token.type != TOKEN_STRING &&
+ current->right->token.type != TOKEN_RE)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01335)
+ "Invalid expression \"%s\" in file %s: Token '-A' must be followed by a URI string.",
+ expr, r->filename);
+ *was_error = 1;
+ return 0;
+ }
+ current->right->token.value =
+ ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
+ SSI_EXPAND_DROP_NAME);
+ rr = ap_sub_req_lookup_uri(current->right->token.value, r, NULL);
+ /* 400 and higher are considered access denied */
+ if (rr->status < HTTP_BAD_REQUEST) {
+ current->value = 1;
+ }
+ else {
+ current->value = 0;
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rr->status, r, APLOGNO(01336)
+ "mod_include: The tested "
+ "subrequest -A \"%s\" returned an error code.",
+ current->right->token.value);
+ }
+ ap_destroy_sub_req(rr);
+ break;
+
+ case TOKEN_RE:
+ if (!error) {
+ error = APLOGNO(03190) "No operator before regex in expr \"%s\" in file %s";
+ }
+ case TOKEN_LBRACE:
+ if (!error) {
+ error = APLOGNO(03191) "Unmatched '(' in \"%s\" in file %s";
+ }
+ default:
+ if (!error) {
+ error = APLOGNO(03192) "internal parser error in \"%s\" in file %s";
+ }
+
+ /* Intentional no APLOGNO */
+ /* error text provides APLOGNO */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, expr, r->filename);
+ *was_error = 1;
+ return 0;
+ }
+
+ DEBUG_DUMP_EVAL(ctx, current);
+ current->done = 1;
+ current = current->parent;
+ }
+
+ return (root ? root->value : 0);
+}
+
+/* same as above, but use common ap_expr syntax / API */
+static int parse_ap_expr(include_ctx_t *ctx, const char *expr, int *was_error)
+{
+ ap_expr_info_t *expr_info = apr_pcalloc(ctx->pool, sizeof (*expr_info));
+ const char *err;
+ int ret;
+ backref_t *re = ctx->intern->re;
+ ap_expr_eval_ctx_t *eval_ctx = ctx->intern->expr_eval_ctx;
+
+ expr_info->filename = ctx->r->filename;
+ expr_info->line_number = 0;
+ expr_info->module_index = APLOG_MODULE_INDEX;
+ expr_info->flags = AP_EXPR_FLAG_RESTRICTED;
+ err = ap_expr_parse(ctx->r->pool, ctx->r->pool, expr_info, expr,
+ include_expr_lookup);
+ if (err) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(01337)
+ "Could not parse expr \"%s\" in %s: %s", expr,
+ ctx->r->filename, err);
+ *was_error = 1;
+ return 0;
+ }
+
+ if (!re) {
+ ctx->intern->re = re = apr_pcalloc(ctx->pool, sizeof(*re));
+ }
+ else {
+ /* ap_expr_exec_ctx() does not care about re->have_match but only about
+ * re->source
+ */
+ if (!re->have_match)
+ re->source = NULL;
+ }
+
+ if (!eval_ctx) {
+ eval_ctx = apr_pcalloc(ctx->pool, sizeof(*eval_ctx));
+ ctx->intern->expr_eval_ctx = eval_ctx;
+ eval_ctx->r = ctx->r;
+ eval_ctx->c = ctx->r->connection;
+ eval_ctx->s = ctx->r->server;
+ eval_ctx->p = ctx->r->pool;
+ eval_ctx->data = ctx;
+ eval_ctx->err = &ctx->intern->expr_err;
+ eval_ctx->vary_this = &ctx->intern->expr_vary_this;
+ eval_ctx->re_nmatch = AP_MAX_REG_MATCH;
+ eval_ctx->re_pmatch = re->match;
+ eval_ctx->re_source = &re->source;
+ }
+
+ eval_ctx->info = expr_info;
+ ret = ap_expr_exec_ctx(eval_ctx);
+ if (ret < 0) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(01338)
+ "Could not evaluate expr \"%s\" in %s: %s", expr,
+ ctx->r->filename, ctx->intern->expr_err);
+ *was_error = 1;
+ return 0;
+ }
+ *was_error = 0;
+ if (re->source)
+ re->have_match = 1;
+ return ret;
+}
+
+/*
+ * +-------------------------------------------------------+
+ * | |
+ * | Action Handlers
+ * | |
+ * +-------------------------------------------------------+
+ */
+
+/*
+ * 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..
+ */
+static void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag,
+ char **tag_val, int dodecode)
+{
+ if (!ctx->intern->argv) {
+ *tag = NULL;
+ *tag_val = NULL;
+
+ return;
+ }
+
+ *tag_val = ctx->intern->argv->value;
+ *tag = ctx->intern->argv->name;
+
+ ctx->intern->argv = ctx->intern->argv->next;
+
+ if (dodecode && *tag_val) {
+ decodehtml(*tag_val);
+ }
+}
+
+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")) {
+ char *newpath;
+
+ /* be safe; only files in this directory or below allowed */
+ rv = apr_filepath_merge(&newpath, NULL, tag_val,
+ APR_FILEPATH_SECUREROOTTEST |
+ APR_FILEPATH_NOTABSOLUTE, r->pool);
+
+ if (rv != APR_SUCCESS) {
+ error_fmt = APLOGNO(02668) "unable to access file \"%s\" "
+ "in parsed file %s";
+ }
+ else {
+ /* 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(newpath, r, NULL);
+
+ if (rr->status == HTTP_OK && rr->finfo.filetype != APR_NOFILE) {
+ 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 = APLOGNO(02669) "unable to get information "
+ "about \"%s\" in parsed file %s";
+ }
+ }
+ else {
+ error_fmt = APLOGNO(02670) "unable to lookup information "
+ "about \"%s\" in parsed file %s";
+ }
+ }
+
+ if (error_fmt) {
+ ret = -1;
+ /* Intentional no APLOGNO */
+ /* error_fmt provides APLOGNO */
+ 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 != APR_NOFILE) {
+ 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, APLOGNO(01339) "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, APLOGNO(01340) "unknown parameter \"%s\" "
+ "to tag %s in %s", tag, directive, r->filename);
+ return -1;
+ }
+}
+
+/*
+ * <!--#comment blah blah blah ... -->
+ */
+static apr_status_t handle_comment(include_ctx_t *ctx, ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+ return APR_SUCCESS;
+}
+
+/*
+ * <!--#include virtual|file="..." [onerror|virtual|file="..."] ... -->
+ *
+ * Output each file/virtual in turn until one of them returns an error.
+ * On error, ignore all further file/virtual attributes until we reach
+ * an onerror attribute, where we make an attempt to serve the onerror
+ * virtual url. If onerror fails, or no onerror is present, the default
+ * error string is inserted into the stream.
+ */
+static apr_status_t handle_include(include_ctx_t *ctx, ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+ request_rec *r = f->r;
+ char *last_error;
+
+ if (!ctx->argc) {
+ ap_log_rerror(APLOG_MARK,
+ (ctx->flags & SSI_FLAG_PRINTING)
+ ? APLOG_ERR : APLOG_WARNING,
+ 0, r, APLOGNO(01341)
+ "missing argument for include element in %s",
+ r->filename);
+ }
+
+ if (!(ctx->flags & SSI_FLAG_PRINTING)) {
+ return APR_SUCCESS;
+ }
+
+ if (!ctx->argc) {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ return APR_SUCCESS;
+ }
+
+ last_error = NULL;
+ while (1) {
+ char *tag = NULL;
+ char *tag_val = NULL;
+ request_rec *rr = NULL;
+ char *error_fmt = NULL;
+ char *parsed_string;
+ apr_status_t rv = APR_SUCCESS;
+ int status = 0;
+
+ ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
+ if (!tag || !tag_val) {
+ break;
+ }
+
+ if (strcmp(tag, "virtual") && strcmp(tag, "file") && strcmp(tag,
+ "onerror")) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01342) "unknown parameter "
+ "\"%s\" to tag include in %s", tag, r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ break;
+ }
+
+ parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
+ SSI_EXPAND_DROP_NAME);
+ if (tag[0] == 'f') {
+ char *newpath;
+
+ /* be safe; only files in this directory or below allowed */
+ rv = apr_filepath_merge(&newpath, NULL, parsed_string,
+ APR_FILEPATH_SECUREROOTTEST |
+ APR_FILEPATH_NOTABSOLUTE, ctx->dpool);
+
+ if (rv != APR_SUCCESS) {
+ error_fmt = "unable to include file \"%s\" in parsed file %s";
+ }
+ else {
+ rr = ap_sub_req_lookup_file(newpath, r, f->next);
+ }
+ }
+ else if ((tag[0] == 'v' && !last_error)
+ || (tag[0] == 'o' && last_error)) {
+ if (r->kept_body) {
+ rr = ap_sub_req_method_uri(r->method, parsed_string, r, f->next);
+ }
+ else {
+ rr = ap_sub_req_lookup_uri(parsed_string, r, f->next);
+ }
+ }
+ else {
+ continue;
+ }
+
+ if (!error_fmt && rr->status != HTTP_OK) {
+ error_fmt = "unable to include \"%s\" in parsed file %s, subrequest setup returned %d";
+ }
+
+ if (!error_fmt && (ctx->flags & SSI_FLAG_NO_EXEC) &&
+ rr->content_type && strncmp(rr->content_type, "text/", 5)) {
+
+ error_fmt = "unable to include potential exec \"%s\" in parsed "
+ "file %s, content type not text/*";
+ }
+
+ /* See the Kludge in includes_filter 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 && ((status = ap_run_sub_req(rr)))) {
+ error_fmt = "unable to include \"%s\" in parsed file %s, subrequest returned %d";
+ }
+
+ if (error_fmt) {
+ /* Intentional no APLOGNO */
+ /* error text is also sent to client */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, error_fmt, tag_val,
+ r->filename, status ? status : rr ? rr->status : 0);
+ if (last_error) {
+ /* onerror threw an error, give up completely */
+ break;
+ }
+ last_error = error_fmt;
+ }
+ else {
+ last_error = NULL;
+ }
+
+ /* 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. */
+
+ }
+
+ if (last_error) {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ }
+
+ return APR_SUCCESS;
+}
+
+/*
+ * <!--#echo [decoding="..."] [encoding="..."] var="..." [decoding="..."]
+ * [encoding="..."] var="..." ... -->
+ */
+static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+ const char *encoding = "entity", *decoding = "none";
+ request_rec *r = f->r;
+ int error = 0;
+
+ if (!ctx->argc) {
+ ap_log_rerror(APLOG_MARK,
+ (ctx->flags & SSI_FLAG_PRINTING)
+ ? APLOG_ERR : APLOG_WARNING,
+ 0, r, APLOGNO(01343)
+ "missing argument for echo element in %s",
+ r->filename);
+ }
+
+ if (!(ctx->flags & SSI_FLAG_PRINTING)) {
+ return APR_SUCCESS;
+ }
+
+ if (!ctx->argc) {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ return APR_SUCCESS;
+ }
+
+ while (1) {
+ char *tag = NULL;
+ char *tag_val = NULL;
+
+ ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
+ if (!tag || !tag_val) {
+ break;
+ }
+
+ if (!strcmp(tag, "var")) {
+ const char *val;
+ const char *echo_text = NULL;
+ apr_size_t e_len;
+
+ val = get_include_var(ap_ssi_parse_string(ctx, tag_val, NULL,
+ 0, SSI_EXPAND_DROP_NAME),
+ ctx);
+
+ if (val) {
+ char *last = NULL;
+ char *e, *d, *token;
+
+ echo_text = val;
+
+ d = apr_pstrdup(ctx->pool, decoding);
+ token = apr_strtok(d, ", \t", &last);
+
+ while (token) {
+ if (!ap_cstr_casecmp(token, "none")) {
+ /* do nothing */
+ }
+ else if (!ap_cstr_casecmp(token, "url")) {
+ char *buf = apr_pstrdup(ctx->pool, echo_text);
+ ap_unescape_url(buf);
+ echo_text = buf;
+ }
+ else if (!ap_cstr_casecmp(token, "urlencoded")) {
+ char *buf = apr_pstrdup(ctx->pool, echo_text);
+ ap_unescape_urlencoded(buf);
+ echo_text = buf;
+ }
+ else if (!ap_cstr_casecmp(token, "entity")) {
+ char *buf = apr_pstrdup(ctx->pool, echo_text);
+ decodehtml(buf);
+ echo_text = buf;
+ }
+ else if (!ap_cstr_casecmp(token, "base64")) {
+ echo_text = ap_pbase64decode(ctx->dpool, echo_text);
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01344) "unknown value "
+ "\"%s\" to parameter \"decoding\" of tag echo in "
+ "%s", token, r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ error = 1;
+ break;
+ }
+ token = apr_strtok(NULL, ", \t", &last);
+ }
+
+ e = apr_pstrdup(ctx->pool, encoding);
+ token = apr_strtok(e, ", \t", &last);
+
+ while (token) {
+ if (!ap_cstr_casecmp(token, "none")) {
+ /* do nothing */
+ }
+ else if (!ap_cstr_casecmp(token, "url")) {
+ echo_text = ap_escape_uri(ctx->dpool, echo_text);
+ }
+ else if (!ap_cstr_casecmp(token, "urlencoded")) {
+ echo_text = ap_escape_urlencoded(ctx->dpool, echo_text);
+ }
+ else if (!ap_cstr_casecmp(token, "entity")) {
+ echo_text = ap_escape_html2(ctx->dpool, echo_text, 0);
+ }
+ else if (!ap_cstr_casecmp(token, "base64")) {
+ char *buf;
+ buf = ap_pbase64encode(ctx->dpool, (char *)echo_text);
+ echo_text = buf;
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01345) "unknown value "
+ "\"%s\" to parameter \"encoding\" of tag echo in "
+ "%s", token, r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ error = 1;
+ break;
+ }
+ token = apr_strtok(NULL, ", \t", &last);
+ }
+
+ e_len = strlen(echo_text);
+ }
+ else {
+ echo_text = ctx->intern->undefined_echo;
+ e_len = ctx->intern->undefined_echo_len;
+ }
+
+ if (error) {
+ break;
+ }
+
+ APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(
+ apr_pmemdup(ctx->pool, echo_text, e_len),
+ e_len, ctx->pool, f->c->bucket_alloc));
+ }
+ else if (!strcmp(tag, "decoding")) {
+ decoding = tag_val;
+ }
+ else if (!strcmp(tag, "encoding")) {
+ encoding = tag_val;
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01346) "unknown parameter "
+ "\"%s\" in tag echo of %s", tag, r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ break;
+ }
+ }
+
+ return APR_SUCCESS;
+}
+
+/*
+ * <!--#config [timefmt="..."] [sizefmt="..."] [errmsg="..."]
+ * [echomsg="..."] -->
+ */
+static apr_status_t handle_config(include_ctx_t *ctx, ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+ request_rec *r = f->r;
+ apr_table_t *env = r->subprocess_env;
+
+ if (!ctx->argc) {
+ ap_log_rerror(APLOG_MARK,
+ (ctx->flags & SSI_FLAG_PRINTING)
+ ? APLOG_ERR : APLOG_WARNING,
+ 0, r, APLOGNO(01347)
+ "missing argument for config element in %s",
+ r->filename);
+ }
+
+ if (!(ctx->flags & SSI_FLAG_PRINTING)) {
+ return APR_SUCCESS;
+ }
+
+ if (!ctx->argc) {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ return APR_SUCCESS;
+ }
+
+ while (1) {
+ char *tag = NULL;
+ char *tag_val = NULL;
+
+ ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_RAW);
+ if (!tag || !tag_val) {
+ break;
+ }
+
+ if (!strcmp(tag, "errmsg")) {
+ ctx->error_str = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
+ SSI_EXPAND_DROP_NAME);
+ }
+ else if (!strcmp(tag, "echomsg")) {
+ ctx->intern->undefined_echo =
+ ap_ssi_parse_string(ctx, tag_val, NULL, 0,SSI_EXPAND_DROP_NAME);
+ ctx->intern->undefined_echo_len=strlen(ctx->intern->undefined_echo);
+ }
+ else if (!strcmp(tag, "timefmt")) {
+ apr_time_t date = r->request_time;
+
+ ctx->time_str = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
+ SSI_EXPAND_DROP_NAME);
+
+ 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")) {
+ char *parsed_string;
+
+ parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
+ SSI_EXPAND_DROP_NAME);
+ if (!strcmp(parsed_string, "bytes")) {
+ ctx->flags |= SSI_FLAG_SIZE_IN_BYTES;
+ }
+ else if (!strcmp(parsed_string, "abbrev")) {
+ ctx->flags &= SSI_FLAG_SIZE_ABBREV;
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01348) "unknown value "
+ "\"%s\" to parameter \"sizefmt\" of tag config "
+ "in %s", parsed_string, r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ break;
+ }
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01349) "unknown parameter "
+ "\"%s\" to tag config in %s", tag, r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ break;
+ }
+ }
+
+ return APR_SUCCESS;
+}
+
+/*
+ * <!--#fsize virtual|file="..." [virtual|file="..."] ... -->
+ */
+static apr_status_t handle_fsize(include_ctx_t *ctx, ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+ request_rec *r = f->r;
+
+ if (!ctx->argc) {
+ ap_log_rerror(APLOG_MARK,
+ (ctx->flags & SSI_FLAG_PRINTING)
+ ? APLOG_ERR : APLOG_WARNING,
+ 0, r, APLOGNO(01350)
+ "missing argument for fsize element in %s",
+ r->filename);
+ }
+
+ if (!(ctx->flags & SSI_FLAG_PRINTING)) {
+ return APR_SUCCESS;
+ }
+
+ if (!ctx->argc) {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ return APR_SUCCESS;
+ }
+
+ while (1) {
+ char *tag = NULL;
+ char *tag_val = NULL;
+ apr_finfo_t finfo;
+ char *parsed_string;
+
+ ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
+ if (!tag || !tag_val) {
+ break;
+ }
+
+ parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
+ SSI_EXPAND_DROP_NAME);
+
+ if (!find_file(r, "fsize", tag, parsed_string, &finfo)) {
+ char *buf;
+ apr_size_t len;
+
+ if (!(ctx->flags & SSI_FLAG_SIZE_IN_BYTES)) {
+ buf = apr_strfsize(finfo.size, apr_palloc(ctx->pool, 5));
+ len = 4; /* omit the \0 terminator */
+ }
+ else {
+ apr_size_t l, x, pos;
+ char *tmp;
+
+ tmp = apr_psprintf(ctx->dpool, "%" APR_OFF_T_FMT, finfo.size);
+ len = l = strlen(tmp);
+
+ for (x = 0; x < l; ++x) {
+ if (x && !((l - x) % 3)) {
+ ++len;
+ }
+ }
+
+ if (len == l) {
+ buf = apr_pstrmemdup(ctx->pool, tmp, len);
+ }
+ else {
+ buf = apr_palloc(ctx->pool, len);
+
+ for (pos = x = 0; x < l; ++x) {
+ if (x && !((l - x) % 3)) {
+ buf[pos++] = ',';
+ }
+ buf[pos++] = tmp[x];
+ }
+ }
+ }
+
+ APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(buf, len,
+ ctx->pool, f->c->bucket_alloc));
+ }
+ else {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ break;
+ }
+ }
+
+ return APR_SUCCESS;
+}
+
+/*
+ * <!--#flastmod virtual|file="..." [virtual|file="..."] ... -->
+ */
+static apr_status_t handle_flastmod(include_ctx_t *ctx, ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+ request_rec *r = f->r;
+
+ if (!ctx->argc) {
+ ap_log_rerror(APLOG_MARK,
+ (ctx->flags & SSI_FLAG_PRINTING)
+ ? APLOG_ERR : APLOG_WARNING,
+ 0, r, APLOGNO(01351)
+ "missing argument for flastmod element in %s",
+ r->filename);
+ }
+
+ if (!(ctx->flags & SSI_FLAG_PRINTING)) {
+ return APR_SUCCESS;
+ }
+
+ if (!ctx->argc) {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ return APR_SUCCESS;
+ }
+
+ while (1) {
+ char *tag = NULL;
+ char *tag_val = NULL;
+ apr_finfo_t finfo;
+ char *parsed_string;
+
+ ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
+ if (!tag || !tag_val) {
+ break;
+ }
+
+ parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
+ SSI_EXPAND_DROP_NAME);
+
+ if (!find_file(r, "flastmod", tag, parsed_string, &finfo)) {
+ char *t_val;
+ apr_size_t t_len;
+
+ t_val = ap_ht_time(ctx->pool, finfo.mtime, ctx->time_str, 0);
+ t_len = strlen(t_val);
+
+ APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(t_val, t_len,
+ ctx->pool, f->c->bucket_alloc));
+ }
+ else {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ break;
+ }
+ }
+
+ return APR_SUCCESS;
+}
+
+/*
+ * <!--#if expr="..." -->
+ */
+static apr_status_t handle_if(include_ctx_t *ctx, ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+ char *tag = NULL;
+ char *expr = NULL;
+ request_rec *r = f->r;
+ int expr_ret, was_error;
+
+ if (ctx->argc != 1) {
+ ap_log_rerror(APLOG_MARK,
+ (ctx->flags & SSI_FLAG_PRINTING)
+ ? APLOG_ERR : APLOG_WARNING,
+ 0, r,
+ (ctx->argc)
+ ? APLOGNO(01352) "too many arguments for if element in %s"
+ : APLOGNO(01353) "missing expr argument for if element in %s",
+ r->filename);
+ }
+
+ if (!(ctx->flags & SSI_FLAG_PRINTING)) {
+ ++(ctx->if_nesting_level);
+ return APR_SUCCESS;
+ }
+
+ if (ctx->argc != 1) {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ return APR_SUCCESS;
+ }
+
+ ap_ssi_get_tag_and_value(ctx, &tag, &expr, SSI_VALUE_RAW);
+
+ if (strcmp(tag, "expr")) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01354) "unknown parameter \"%s\" "
+ "to tag if in %s", tag, r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ return APR_SUCCESS;
+ }
+
+ if (!expr) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01355) "missing expr value for if "
+ "element in %s", r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ return APR_SUCCESS;
+ }
+
+ DEBUG_PRINTF((ctx, "**** if expr=\"%s\"\n", expr));
+
+ if (ctx->intern->legacy_expr)
+ expr_ret = parse_expr(ctx, expr, &was_error);
+ else
+ expr_ret = parse_ap_expr(ctx, expr, &was_error);
+
+ if (was_error) {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ return APR_SUCCESS;
+ }
+
+ if (expr_ret) {
+ ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
+ }
+ else {
+ ctx->flags &= SSI_FLAG_CLEAR_PRINT_COND;
+ }
+
+ DEBUG_DUMP_COND(ctx, " if");
+
+ ctx->if_nesting_level = 0;
+
+ return APR_SUCCESS;
+}
+
+/*
+ * <!--#elif expr="..." -->
+ */
+static apr_status_t handle_elif(include_ctx_t *ctx, ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+ char *tag = NULL;
+ char *expr = NULL;
+ request_rec *r = f->r;
+ int expr_ret, was_error;
+
+ if (ctx->argc != 1) {
+ ap_log_rerror(APLOG_MARK,
+ (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
+ 0, r,
+ (ctx->argc)
+ ? APLOGNO(01356) "too many arguments for if element in %s"
+ : APLOGNO(01357) "missing expr argument for if element in %s",
+ r->filename);
+ }
+
+ if (ctx->if_nesting_level) {
+ return APR_SUCCESS;
+ }
+
+ if (ctx->argc != 1) {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ return APR_SUCCESS;
+ }
+
+ ap_ssi_get_tag_and_value(ctx, &tag, &expr, SSI_VALUE_RAW);
+
+ if (strcmp(tag, "expr")) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01358) "unknown parameter \"%s\" "
+ "to tag if in %s", tag, r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ return APR_SUCCESS;
+ }
+
+ if (!expr) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01359) "missing expr in elif "
+ "statement: %s", r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ return APR_SUCCESS;
+ }
+
+ DEBUG_PRINTF((ctx, "**** elif expr=\"%s\"\n", expr));
+ DEBUG_DUMP_COND(ctx, " elif");
+
+ if (ctx->flags & SSI_FLAG_COND_TRUE) {
+ ctx->flags &= SSI_FLAG_CLEAR_PRINTING;
+ return APR_SUCCESS;
+ }
+
+ if (ctx->intern->legacy_expr)
+ expr_ret = parse_expr(ctx, expr, &was_error);
+ else
+ expr_ret = parse_ap_expr(ctx, expr, &was_error);
+
+ if (was_error) {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ return APR_SUCCESS;
+ }
+
+ if (expr_ret) {
+ ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
+ }
+ else {
+ ctx->flags &= SSI_FLAG_CLEAR_PRINT_COND;
+ }
+
+ DEBUG_DUMP_COND(ctx, " elif");
+
+ return APR_SUCCESS;
+}
+
+/*
+ * <!--#else -->
+ */
+static apr_status_t handle_else(include_ctx_t *ctx, ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+ request_rec *r = f->r;
+
+ if (ctx->argc) {
+ ap_log_rerror(APLOG_MARK,
+ (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
+ 0, r, APLOGNO(01360)
+ "else directive does not take tags in %s",
+ r->filename);
+ }
+
+ if (ctx->if_nesting_level) {
+ return APR_SUCCESS;
+ }
+
+ if (ctx->argc) {
+ if (ctx->flags & SSI_FLAG_PRINTING) {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ }
+
+ return APR_SUCCESS;
+ }
+
+ DEBUG_DUMP_COND(ctx, " else");
+
+ if (ctx->flags & SSI_FLAG_COND_TRUE) {
+ ctx->flags &= SSI_FLAG_CLEAR_PRINTING;
+ }
+ else {
+ ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
+ }
+
+ return APR_SUCCESS;
+}
+
+/*
+ * <!--#endif -->
+ */
+static apr_status_t handle_endif(include_ctx_t *ctx, ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+ request_rec *r = f->r;
+
+ if (ctx->argc) {
+ ap_log_rerror(APLOG_MARK,
+ (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING,
+ 0, r, APLOGNO(01361)
+ "endif directive does not take tags in %s",
+ r->filename);
+ }
+
+ if (ctx->if_nesting_level) {
+ --(ctx->if_nesting_level);
+ return APR_SUCCESS;
+ }
+
+ if (ctx->argc) {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ return APR_SUCCESS;
+ }
+
+ DEBUG_DUMP_COND(ctx, "endif");
+
+ ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
+
+ return APR_SUCCESS;
+}
+
+/*
+ * <!--#set var="..." value="..." ... -->
+ */
+static apr_status_t handle_set(include_ctx_t *ctx, ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+ const char *encoding = "none", *decoding = "none";
+ char *var = NULL;
+ request_rec *r = f->r;
+ request_rec *sub = r->main;
+ apr_pool_t *p = r->pool;
+ int error = 0;
+
+ if (ctx->argc < 2) {
+ ap_log_rerror(APLOG_MARK,
+ (ctx->flags & SSI_FLAG_PRINTING)
+ ? APLOG_ERR : APLOG_WARNING,
+ 0, r,
+ APLOGNO(01362) "missing argument for set element in %s",
+ r->filename);
+ }
+
+ if (!(ctx->flags & SSI_FLAG_PRINTING)) {
+ return APR_SUCCESS;
+ }
+
+ if (ctx->argc < 2) {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ return APR_SUCCESS;
+ }
+
+ /* 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;
+ }
+
+ while (1) {
+ char *tag = NULL;
+ char *tag_val = NULL;
+
+ ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_RAW);
+
+ if (!tag || !tag_val) {
+ break;
+ }
+
+ if (!strcmp(tag, "var")) {
+ decodehtml(tag_val);
+ var = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
+ SSI_EXPAND_DROP_NAME);
+ }
+ else if (!strcmp(tag, "decoding")) {
+ decoding = tag_val;
+ }
+ else if (!strcmp(tag, "encoding")) {
+ encoding = tag_val;
+ }
+ else if (!strcmp(tag, "value")) {
+ char *parsed_string;
+
+ if (!var) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01363) "variable must "
+ "precede value in set directive in %s",
+ r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ break;
+ }
+
+ parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
+ SSI_EXPAND_DROP_NAME);
+
+ if (parsed_string) {
+ char *last = NULL;
+ char *e, *d, *token;
+
+ d = apr_pstrdup(ctx->pool, decoding);
+ token = apr_strtok(d, ", \t", &last);
+
+ while (token) {
+ if (!ap_cstr_casecmp(token, "none")) {
+ /* do nothing */
+ }
+ else if (!ap_cstr_casecmp(token, "url")) {
+ char *buf = apr_pstrdup(ctx->pool, parsed_string);
+ ap_unescape_url(buf);
+ parsed_string = buf;
+ }
+ else if (!ap_cstr_casecmp(token, "urlencoded")) {
+ char *buf = apr_pstrdup(ctx->pool, parsed_string);
+ ap_unescape_urlencoded(buf);
+ parsed_string = buf;
+ }
+ else if (!ap_cstr_casecmp(token, "entity")) {
+ char *buf = apr_pstrdup(ctx->pool, parsed_string);
+ decodehtml(buf);
+ parsed_string = buf;
+ }
+ else if (!ap_cstr_casecmp(token, "base64")) {
+ parsed_string = ap_pbase64decode(ctx->dpool, parsed_string);
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01364) "unknown value "
+ "\"%s\" to parameter \"decoding\" of tag set in "
+ "%s", token, r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ error = 1;
+ break;
+ }
+ token = apr_strtok(NULL, ", \t", &last);
+ }
+
+ e = apr_pstrdup(ctx->pool, encoding);
+ token = apr_strtok(e, ", \t", &last);
+
+ while (token) {
+ if (!ap_cstr_casecmp(token, "none")) {
+ /* do nothing */
+ }
+ else if (!ap_cstr_casecmp(token, "url")) {
+ parsed_string = ap_escape_uri(ctx->dpool, parsed_string);
+ }
+ else if (!ap_cstr_casecmp(token, "urlencoded")) {
+ parsed_string = ap_escape_urlencoded(ctx->dpool, parsed_string);
+ }
+ else if (!ap_cstr_casecmp(token, "entity")) {
+ parsed_string = ap_escape_html2(ctx->dpool, parsed_string, 0);
+ }
+ else if (!ap_cstr_casecmp(token, "base64")) {
+ char *buf;
+ buf = ap_pbase64encode(ctx->dpool, (char *)parsed_string);
+ parsed_string = buf;
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01365) "unknown value "
+ "\"%s\" to parameter \"encoding\" of tag set in "
+ "%s", token, r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ error = 1;
+ break;
+ }
+ token = apr_strtok(NULL, ", \t", &last);
+ }
+
+ }
+
+ if (error) {
+ break;
+ }
+
+ 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, APLOGNO(01366) "Invalid tag for set "
+ "directive in %s", r->filename);
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ break;
+ }
+ }
+
+ return APR_SUCCESS;
+}
+
+/*
+ * <!--#printenv -->
+ */
+static apr_status_t handle_printenv(include_ctx_t *ctx, ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+ request_rec *r = f->r;
+ const apr_array_header_t *arr;
+ const apr_table_entry_t *elts;
+ int i;
+
+ if (ctx->argc) {
+ ap_log_rerror(APLOG_MARK,
+ (ctx->flags & SSI_FLAG_PRINTING)
+ ? APLOG_ERR : APLOG_WARNING,
+ 0, r,
+ APLOGNO(01367) "printenv directive does not take tags in %s",
+ r->filename);
+ }
+
+ if (!(ctx->flags & SSI_FLAG_PRINTING)) {
+ return APR_SUCCESS;
+ }
+
+ if (ctx->argc) {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
+ return APR_SUCCESS;
+ }
+
+ arr = apr_table_elts(r->subprocess_env);
+ elts = (apr_table_entry_t *)arr->elts;
+
+ for (i = 0; i < arr->nelts; ++i) {
+ const char *key_text, *val_text;
+
+ /* get key */
+ key_text = ap_escape_html(ctx->dpool, elts[i].key);
+
+ /* get value */
+ val_text = elts[i].val;
+ if (val_text == LAZY_VALUE)
+ val_text = add_include_vars_lazy(r, elts[i].key, ctx->time_str);
+ val_text = ap_escape_html(ctx->dpool, val_text);
+
+ apr_brigade_putstrs(bb, NULL, NULL, key_text, "=", val_text, "\n",
+ NULL);
+ }
+
+ ctx->flush_now = 1;
+ return APR_SUCCESS;
+}
+
+
+/*
+ * +-------------------------------------------------------+
+ * | |
+ * | Main Includes-Filter Engine
+ * | |
+ * +-------------------------------------------------------+
+ */
+
+/* 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 bndm_t *bndm_compile(apr_pool_t *pool, const char *n, apr_size_t nl)
+{
+ unsigned int x;
+ const char *ne = n + nl;
+ bndm_t *t = apr_palloc(pool, sizeof(*t));
+
+ memset(t->T, 0, sizeof(unsigned int) * 256);
+ t->pattern_len = nl;
+
+ for (x = 1; n < ne; x <<= 1) {
+ t->T[(unsigned char) *n++] |= x;
+ }
+
+ t->x = x - 1;
+
+ return t;
+}
+
+/* Implements the BNDM search algorithm (as described above).
+ *
+ * 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(bndm_t *t, const char *h, apr_size_t hl)
+{
+ const char *skip;
+ const char *he, *p, *pi;
+ unsigned int *T, x, d;
+ apr_size_t nl;
+
+ he = h + hl;
+
+ T = t->T;
+ x = t->x;
+ nl = t->pattern_len;
+
+ 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;
+}
+
+/*
+ * 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(include_ctx_t *ctx, const char *data,
+ apr_size_t len)
+{
+ struct ssi_internal_ctx *intern = ctx->intern;
+ apr_size_t slen = intern->start_seq_pat->pattern_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(intern->start_seq_pat, data, len);
+
+ /* wow, found it. ready. */
+ if (index < len) {
+ intern->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 != *intern->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 == intern->start_seq[pos]) {
+ ++p;
+ ++pos;
+ }
+
+ /* partial match found. Store the info for the next round */
+ if (p == ep) {
+ intern->state = PARSE_HEAD;
+ intern->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(include_ctx_t *ctx,
+ const char *data,
+ apr_size_t len,
+ apr_size_t *release)
+{
+ struct ssi_internal_ctx *intern = ctx->intern;
+ apr_size_t pos, spos = 0;
+ apr_size_t slen = intern->start_seq_pat->pattern_len;
+ const char *p, *ep;
+
+ pos = intern->parse_pos;
+ ep = data + len;
+ *release = 0;
+
+ do {
+ p = data;
+
+ while (p < ep && pos < slen && *p == intern->start_seq[pos]) {
+ ++p;
+ ++pos;
+ }
+
+ /* full match */
+ if (pos == slen) {
+ intern->state = PARSE_DIRECTIVE;
+ return (p - data);
+ }
+
+ /* the whole buffer is a partial match */
+ if (p == ep) {
+ intern->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 < intern->parse_pos) {
+ do {
+ ++spos;
+ ++*release;
+ p = intern->start_seq + spos;
+ pos = intern->parse_pos - spos;
+
+ while (pos && *p != *intern->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 == intern->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 = intern->parse_pos;
+ intern->state = PARSE_PRE_HEAD;
+ return 0;
+}
+
+/*
+ * returns the position after the directive
+ */
+static apr_size_t find_directive(include_ctx_t *ctx, const char *data,
+ apr_size_t len, char ***store,
+ apr_size_t **store_len)
+{
+ struct ssi_internal_ctx *intern = ctx->intern;
+ const char *p = data;
+ const char *ep = data + len;
+ apr_size_t pos;
+
+ switch (intern->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 == *intern->end_seq) {
+ intern->state = PARSE_DIRECTIVE_TAIL;
+ intern->parse_pos = 1;
+ ++p;
+ return (p - data);
+ }
+ ++p;
+ }
+
+ if (p < ep) { /* found delimiter whitespace */
+ intern->state = PARSE_DIRECTIVE_POSTNAME;
+ *store = &intern->directive;
+ *store_len = &intern->directive_len;
+ }
+
+ break;
+
+ case PARSE_DIRECTIVE_TAIL:
+ pos = intern->parse_pos;
+
+ while (p < ep && pos < intern->end_seq_len &&
+ *p == intern->end_seq[pos]) {
+ ++p;
+ ++pos;
+ }
+
+ /* full match, we're done */
+ if (pos == intern->end_seq_len) {
+ intern->state = PARSE_DIRECTIVE_POSTTAIL;
+ *store = &intern->directive;
+ *store_len = &intern->directive_len;
+ break;
+ }
+
+ /* partial match, the buffer is too small to match fully */
+ if (p == ep) {
+ intern->parse_pos = pos;
+ break;
+ }
+
+ /* no match. continue normal parsing */
+ intern->state = PARSE_DIRECTIVE;
+ return 0;
+
+ case PARSE_DIRECTIVE_POSTTAIL:
+ intern->state = PARSE_EXECUTE;
+ intern->directive_len -= intern->end_seq_len;
+ /* continue immediately with the next state */
+
+ case PARSE_DIRECTIVE_POSTNAME:
+ if (PARSE_DIRECTIVE_POSTNAME == intern->state) {
+ intern->state = PARSE_PRE_ARG;
+ }
+ ctx->argc = 0;
+ intern->argv = NULL;
+
+ if (!intern->directive_len) {
+ intern->error = 1;
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(01368) "missing "
+ "directive name in parsed document %s",
+ ctx->r->filename);
+ }
+ else {
+ char *sp = intern->directive;
+ char *sep = intern->directive + intern->directive_len;
+
+ /* 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(include_ctx_t *ctx, const char *data,
+ apr_size_t len)
+{
+ struct ssi_internal_ctx *intern = ctx->intern;
+ 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) {
+ intern->state = (*p == *intern->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(include_ctx_t *ctx, const char *data,
+ apr_size_t len)
+{
+ struct ssi_internal_ctx *intern = ctx->intern;
+ const char *p = data;
+ const char *ep = data + len;
+ apr_size_t pos = intern->parse_pos;
+
+ if (PARSE_TAIL == intern->state) {
+ intern->state = PARSE_TAIL_SEQ;
+ pos = intern->parse_pos = 0;
+ }
+
+ while (p < ep && pos < intern->end_seq_len && *p == intern->end_seq[pos]) {
+ ++p;
+ ++pos;
+ }
+
+ /* bingo, full match */
+ if (pos == intern->end_seq_len) {
+ intern->state = PARSE_EXECUTE;
+ return (p - data);
+ }
+
+ /* partial match, the buffer is too small to match fully */
+ if (p == ep) {
+ intern->parse_pos = pos;
+ return (p - data);
+ }
+
+ /* no match. It must be an argument string then
+ * The caller should cleanup and rewind to the reparse point
+ */
+ intern->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(include_ctx_t *ctx, const char *data,
+ apr_size_t len, char ***store,
+ apr_size_t **store_len)
+{
+ struct ssi_internal_ctx *intern = ctx->intern;
+ const char *p = data;
+ const char *ep = data + len;
+
+ switch (intern->state) {
+ case PARSE_ARG:
+ /*
+ * create argument structure and append it to the current list
+ */
+ intern->current_arg = apr_palloc(ctx->dpool,
+ sizeof(*intern->current_arg));
+ intern->current_arg->next = NULL;
+
+ ++(ctx->argc);
+ if (!intern->argv) {
+ intern->argv = intern->current_arg;
+ }
+ else {
+ arg_item_t *newarg = intern->argv;
+
+ while (newarg->next) {
+ newarg = newarg->next;
+ }
+ newarg->next = intern->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;
+
+ intern->state = PARSE_ARG_VAL;
+ intern->quote = *p++;
+ intern->current_arg->name = NULL;
+ intern->current_arg->name_len = 0;
+ intern->error = 1;
+
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(01369) "missing "
+ "argument name for value to tag %s in %s",
+ apr_pstrmemdup(ctx->r->pool, intern->directive,
+ intern->directive_len),
+ ctx->r->filename);
+
+ return (p - data);
+
+ default:
+ intern->state = PARSE_ARG_NAME;
+ }
+ /* continue immediately with next state */
+
+ case PARSE_ARG_NAME:
+ while (p < ep && !apr_isspace(*p) && *p != '=') {
+ ++p;
+ }
+
+ if (p < ep) {
+ intern->state = PARSE_ARG_POSTNAME;
+ *store = &intern->current_arg->name;
+ *store_len = &intern->current_arg->name_len;
+ return (p - data);
+ }
+ break;
+
+ case PARSE_ARG_POSTNAME:
+ intern->current_arg->name = apr_pstrmemdup(ctx->dpool,
+ intern->current_arg->name,
+ intern->current_arg->name_len);
+ if (!intern->current_arg->name_len) {
+ intern->error = 1;
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(01370) "missing "
+ "argument name for value to tag %s in %s",
+ apr_pstrmemdup(ctx->r->pool, intern->directive,
+ intern->directive_len),
+ ctx->r->filename);
+ }
+ else {
+ ap_str_tolower(intern->current_arg->name);
+ }
+
+ intern->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 == '=') {
+ intern->state = PARSE_ARG_PREVAL;
+ ++p;
+ }
+ else { /* no value */
+ intern->current_arg->value = NULL;
+ intern->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) {
+ intern->state = PARSE_ARG_VAL;
+ switch (*p) {
+ case '"': case '\'': case '`':
+ intern->quote = *p++;
+ break;
+ default:
+ intern->quote = '\0';
+ break;
+ }
+
+ return (p - data);
+ }
+ break;
+
+ case PARSE_ARG_VAL_ESC:
+ if (*p == intern->quote) {
+ ++p;
+ }
+ intern->state = PARSE_ARG_VAL;
+ /* continue with next state immediately */
+
+ case PARSE_ARG_VAL:
+ for (; p < ep; ++p) {
+ if (intern->quote && *p == '\\') {
+ ++p;
+ if (p == ep) {
+ intern->state = PARSE_ARG_VAL_ESC;
+ break;
+ }
+
+ if (*p != intern->quote) {
+ --p;
+ }
+ }
+ else if (intern->quote && *p == intern->quote) {
+ ++p;
+ *store = &intern->current_arg->value;
+ *store_len = &intern->current_arg->value_len;
+ intern->state = PARSE_ARG_POSTVAL;
+ break;
+ }
+ else if (!intern->quote && apr_isspace(*p)) {
+ ++p;
+ *store = &intern->current_arg->value;
+ *store_len = &intern->current_arg->value_len;
+ intern->state = PARSE_ARG_POSTVAL;
+ break;
+ }
+ }
+
+ return (p - data);
+
+ case PARSE_ARG_POSTVAL:
+ /*
+ * The value is still the raw input string. Finally clean it up.
+ */
+ --(intern->current_arg->value_len);
+
+ /* strip quote escaping \ from the string */
+ if (intern->quote) {
+ apr_size_t shift = 0;
+ char *sp;
+
+ sp = intern->current_arg->value;
+ ep = intern->current_arg->value + intern->current_arg->value_len;
+ while (sp < ep && *sp != '\\') {
+ ++sp;
+ }
+ for (; sp < ep; ++sp) {
+ if (*sp == '\\' && sp[1] == intern->quote) {
+ ++sp;
+ ++shift;
+ }
+ if (shift) {
+ *(sp-shift) = *sp;
+ }
+ }
+
+ intern->current_arg->value_len -= shift;
+ }
+
+ intern->current_arg->value[intern->current_arg->value_len] = '\0';
+ intern->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)
+{
+ include_ctx_t *ctx = f->ctx;
+ struct ssi_internal_ctx *intern = ctx->intern;
+ 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 (intern->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->pool, f->c->bucket_alloc);
+
+ /* initialization for this loop */
+ intern->bytes_read = 0;
+ intern->error = 0;
+ ctx->flush_now = 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 = NULL;
+
+ /* 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)) {
+ intern->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 == intern->state ||
+ PARSE_DIRECTIVE_POSTTAIL == intern->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->flush_now = 1;
+ }
+
+ b = newb;
+ continue;
+ }
+ }
+
+ /* enough is enough ... */
+ if (ctx->flush_now ||
+ intern->bytes_read > AP_MIN_BYTES_TO_WRITE) {
+
+ if (!APR_BRIGADE_EMPTY(pass_bb)) {
+ rv = ap_pass_brigade(f->next, pass_bb);
+ if (rv != APR_SUCCESS) {
+ apr_brigade_destroy(pass_bb);
+ return rv;
+ }
+ }
+
+ ctx->flush_now = 0;
+ intern->bytes_read = 0;
+ }
+
+ /* read the current bucket data */
+ len = 0;
+ if (!intern->seen_eos) {
+ if (intern->bytes_read > 0) {
+ rv = apr_bucket_read(b, &data, &len, APR_NONBLOCK_READ);
+ if (APR_STATUS_IS_EAGAIN(rv)) {
+ ctx->flush_now = 1;
+ continue;
+ }
+ }
+
+ if (!len || rv != APR_SUCCESS) {
+ rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
+ }
+
+ if (rv != APR_SUCCESS) {
+ apr_brigade_destroy(pass_bb);
+ return rv;
+ }
+
+ intern->bytes_read += len;
+ }
+
+ /* zero length bucket, fetch next one */
+ if (!len && !intern->seen_eos) {
+ b = APR_BUCKET_NEXT(b);
+ continue;
+ }
+
+ /*
+ * it's actually a data containing bucket, start/continue parsing
+ */
+
+ switch (intern->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->flags & SSI_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 == intern->state) { /* full match */
+ apr_bucket_split(newb, intern->start_seq_pat->pattern_len);
+ ctx->flush_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->flags & SSI_FLAG_PRINTING)) {
+ char *to_release = apr_pmemdup(ctx->pool, intern->start_seq, release);
+
+ newb = apr_bucket_pool_create(to_release, release, 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 == intern->state) { /* final match */
+ apr_bucket_split(b, index);
+ ctx->flush_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_bucket_setaside(b, r->pool);
+ APR_BRIGADE_INSERT_TAIL(intern->tmp_bb, b);
+ b = newb;
+ }
+
+ /* time for cleanup? */
+ if (store != &magic) {
+ apr_brigade_pflatten(intern->tmp_bb, store, store_len,
+ ctx->dpool);
+ apr_brigade_cleanup(intern->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_bucket_setaside(b, r->pool);
+ APR_BRIGADE_INSERT_TAIL(intern->tmp_bb, b);
+ b = newb;
+ }
+
+ /* time for cleanup? */
+ if (store != &magic) {
+ apr_brigade_pflatten(intern->tmp_bb, store, store_len,
+ ctx->dpool);
+ apr_brigade_cleanup(intern->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 (intern->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, intern->tmp_bb);
+ b = APR_BRIGADE_FIRST(bb);
+ break;
+
+ default: /* partial match */
+ newb = APR_BUCKET_NEXT(b);
+ APR_BUCKET_REMOVE(b);
+ apr_bucket_setaside(b, r->pool);
+ APR_BRIGADE_INSERT_TAIL(intern->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 (intern->error) {
+ if (ctx->flags & SSI_FLAG_PRINTING) {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, pass_bb);
+ intern->error = 0;
+ }
+ }
+ else {
+ include_handler_fn_t *handle_func;
+
+ handle_func =
+ (include_handler_fn_t *)apr_hash_get(include_handlers, intern->directive,
+ intern->directive_len);
+
+ if (handle_func) {
+ DEBUG_INIT(ctx, f, pass_bb);
+ rv = handle_func(ctx, f, pass_bb);
+ if (rv != APR_SUCCESS) {
+ apr_brigade_destroy(pass_bb);
+ return rv;
+ }
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01371)
+ "unknown directive \"%s\" in parsed doc %s",
+ apr_pstrmemdup(r->pool, intern->directive,
+ intern->directive_len),
+ r->filename);
+ if (ctx->flags & SSI_FLAG_PRINTING) {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, pass_bb);
+ }
+ }
+ }
+
+ /* cleanup */
+ apr_pool_clear(ctx->dpool);
+ apr_brigade_cleanup(intern->tmp_bb);
+
+ /* Oooof. Done here, start next round */
+ intern->state = PARSE_PRE_HEAD;
+ break;
+
+ } /* switch(ctx->state) */
+
+ } /* while (brigade) */
+
+ /* End of stream. Final cleanup */
+ if (intern->seen_eos) {
+ if (PARSE_HEAD == intern->state) {
+ if (ctx->flags & SSI_FLAG_PRINTING) {
+ char *to_release = apr_pmemdup(ctx->pool, intern->start_seq,
+ intern->parse_pos);
+
+ APR_BRIGADE_INSERT_TAIL(pass_bb,
+ apr_bucket_pool_create(to_release,
+ intern->parse_pos, ctx->pool,
+ f->c->bucket_alloc));
+ }
+ }
+ else if (PARSE_PRE_HEAD != intern->state) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01372)
+ "SSI directive was not properly finished at the end "
+ "of parsed document %s", r->filename);
+ if (ctx->flags & SSI_FLAG_PRINTING) {
+ SSI_CREATE_ERROR_BUCKET(ctx, f, pass_bb);
+ }
+ }
+
+ if (!(ctx->flags & SSI_FLAG_PRINTING)) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01373)
+ "missing closing endif directive in parsed document"
+ " %s", r->filename);
+ }
+
+ /* cleanup our temporary memory */
+ apr_brigade_destroy(intern->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;
+}
+
+
+/*
+ * +-------------------------------------------------------+
+ * | |
+ * | Runtime Hooks
+ * | |
+ * +-------------------------------------------------------+
+ */
+
+static int includes_setup(ap_filter_t *f)
+{
+ include_dir_config *conf = 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.
+ */
+ if (conf->etag <= 0) {
+ 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;
+ request_rec *parent;
+ include_dir_config *conf = 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, APLOGNO(01374)
+ "mod_include: Options +Includes (or IncludesNoExec) "
+ "wasn't set, INCLUDES filter removed: %s", r->uri);
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, b);
+ }
+
+ if (!f->ctx) {
+ struct ssi_internal_ctx *intern;
+ include_ctx_t *ctx;
+
+ /* create context for this filter */
+ f->ctx = ctx = apr_palloc(r->pool, sizeof(*ctx));
+ ctx->r = r;
+ ctx->intern = intern = apr_palloc(r->pool, sizeof(*ctx->intern));
+ ctx->pool = r->pool;
+ apr_pool_create(&ctx->dpool, ctx->pool);
+ apr_pool_tag(ctx->dpool, "includes_dpool");
+
+ /* runtime data */
+ intern->tmp_bb = apr_brigade_create(ctx->pool, f->c->bucket_alloc);
+ intern->seen_eos = 0;
+ intern->state = PARSE_PRE_HEAD;
+ ctx->flags = (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
+ if ((ap_allow_options(r) & OPT_INC_WITH_EXEC) == 0) {
+ ctx->flags |= SSI_FLAG_NO_EXEC;
+ }
+ intern->legacy_expr = (conf->legacy_expr > 0);
+ intern->expr_eval_ctx = NULL;
+ intern->expr_err = NULL;
+ intern->expr_vary_this = NULL;
+
+ ctx->if_nesting_level = 0;
+ intern->re = NULL;
+
+ ctx->error_str = conf->default_error_msg ? conf->default_error_msg :
+ DEFAULT_ERROR_MSG;
+ ctx->time_str = conf->default_time_fmt ? conf->default_time_fmt :
+ DEFAULT_TIME_FORMAT;
+ intern->start_seq = sconf->default_start_tag;
+ intern->start_seq_pat = bndm_compile(ctx->pool, intern->start_seq,
+ strlen(intern->start_seq));
+ intern->end_seq = sconf->default_end_tag;
+ intern->end_seq_len = strlen(intern->end_seq);
+ intern->undefined_echo = conf->undefined_echo ? conf->undefined_echo :
+ DEFAULT_UNDEFINED_ECHO;
+ intern->undefined_echo_len = strlen(intern->undefined_echo);
+ }
+
+ 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);
+ }
+ /* 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.
+ *
+ * SSILastModified on means we *should* set the Last-Modified field
+ * if not present, or respect an existing value if present.
+ */
+
+ /* Must we respect the last modified header? By default, no */
+ if (conf->lastmodified > 0) {
+
+ /* update the last modified if we have a valid time, and only if
+ * we don't already have a valid last modified.
+ */
+ if (r->finfo.valid & APR_FINFO_MTIME
+ && !apr_table_get(f->r->headers_out, "Last-Modified")) {
+ ap_update_mtime(r, r->finfo.mtime);
+ ap_set_last_modified(r);
+ }
+
+ }
+
+ /* Assure the platform supports Group protections */
+ else if (((conf->xbithack == XBITHACK_FULL ||
+ (conf->xbithack == XBITHACK_UNSET &&
+ DEFAULT_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 int include_fixup(request_rec *r)
+{
+ 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
+ {
+ include_dir_config *conf = ap_get_module_config(r->per_dir_config,
+ &include_module);
+
+ if (conf->xbithack == XBITHACK_OFF ||
+ (DEFAULT_XBITHACK == XBITHACK_OFF &&
+ conf->xbithack == XBITHACK_UNSET))
+ {
+ return DECLINED;
+ }
+
+ if (!(r->finfo.protection & APR_UEXECUTE)) {
+ return DECLINED;
+ }
+
+ if (!r->content_type || strncmp(r->content_type, "text/html", 9)) {
+ 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;
+}
+
+
+/*
+ * +-------------------------------------------------------+
+ * | |
+ * | Configuration Handling
+ * | |
+ * +-------------------------------------------------------+
+ */
+
+static void *create_includes_dir_config(apr_pool_t *p, char *dummy)
+{
+ include_dir_config *result = apr_pcalloc(p, sizeof(include_dir_config));
+
+ result->xbithack = XBITHACK_UNSET;
+ result->lastmodified = UNSET;
+ result->etag = UNSET;
+ result->legacy_expr = UNSET;
+
+ return result;
+}
+
+#define MERGE(b,o,n,val,unset) n->val = o->val != unset ? o->val : b->val
+static void *merge_includes_dir_config(apr_pool_t *p, void *basev, void *overridesv)
+{
+ include_dir_config *base = (include_dir_config *)basev,
+ *over = (include_dir_config *)overridesv,
+ *new = apr_palloc(p, sizeof(include_dir_config));
+ MERGE(base, over, new, default_error_msg, NULL);
+ MERGE(base, over, new, default_time_fmt, NULL);
+ MERGE(base, over, new, undefined_echo, NULL);
+ MERGE(base, over, new, xbithack, XBITHACK_UNSET);
+ MERGE(base, over, new, lastmodified, UNSET);
+ MERGE(base, over, new, etag, UNSET);
+ MERGE(base, over, new, legacy_expr, UNSET);
+ return new;
+}
+
+static void *create_includes_server_config(apr_pool_t *p, server_rec *server)
+{
+ include_server_config *result;
+
+ result = apr_palloc(p, sizeof(include_server_config));
+ result->default_end_tag = DEFAULT_END_SEQUENCE;
+ result->default_start_tag = DEFAULT_START_SEQUENCE;
+
+ return result;
+}
+
+static const char *set_xbithack(cmd_parms *cmd, void *mconfig, const char *arg)
+{
+ include_dir_config *conf = mconfig;
+
+ 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 const char *set_default_start_tag(cmd_parms *cmd, void *mconfig,
+ const char *tag)
+{
+ include_server_config *conf;
+ const char *p = tag;
+
+ /* be consistent. (See below in set_default_end_tag) */
+ while (*p) {
+ if (apr_isspace(*p)) {
+ return "SSIStartTag may not contain any whitespaces";
+ }
+ ++p;
+ }
+
+ conf= ap_get_module_config(cmd->server->module_config , &include_module);
+ conf->default_start_tag = tag;
+
+ return NULL;
+}
+
+static const char *set_default_end_tag(cmd_parms *cmd, void *mconfig,
+ const char *tag)
+{
+ include_server_config *conf;
+ const char *p = tag;
+
+ /* sanity check. The parser may fail otherwise */
+ while (*p) {
+ if (apr_isspace(*p)) {
+ return "SSIEndTag may not contain any whitespaces";
+ }
+ ++p;
+ }
+
+ conf= ap_get_module_config(cmd->server->module_config , &include_module);
+ conf->default_end_tag = tag;
+
+ return NULL;
+}
+
+static const char *set_undefined_echo(cmd_parms *cmd, void *mconfig,
+ const char *msg)
+{
+ include_dir_config *conf = mconfig;
+ conf->undefined_echo = msg;
+
+ return NULL;
+}
+
+static const char *set_default_error_msg(cmd_parms *cmd, void *mconfig,
+ const char *msg)
+{
+ include_dir_config *conf = mconfig;
+ conf->default_error_msg = msg;
+
+ return NULL;
+}
+
+static const char *set_default_time_fmt(cmd_parms *cmd, void *mconfig,
+ const char *fmt)
+{
+ include_dir_config *conf = mconfig;
+ conf->default_time_fmt = fmt;
+
+ return NULL;
+}
+
+
+/*
+ * +-------------------------------------------------------+
+ * | |
+ * | Module Initialization and Configuration
+ * | |
+ * +-------------------------------------------------------+
+ */
+
+static int include_post_config(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ include_handlers = 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("comment", handle_comment);
+ ssi_pfn_register("include", handle_include);
+ ssi_pfn_register("flastmod", handle_flastmod);
+ ssi_pfn_register("printenv", handle_printenv);
+ }
+
+ return OK;
+}
+
+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, OR_ALL,
+ "String to be displayed if an echoed variable is undefined"),
+ AP_INIT_FLAG("SSILegacyExprParser", ap_set_flag_slot_char,
+ (void *)APR_OFFSETOF(include_dir_config, legacy_expr),
+ OR_LIMIT,
+ "Whether to use the legacy expression parser compatible "
+ "with <= 2.2.x. Limited to 'on' or 'off'"),
+ AP_INIT_FLAG("SSILastModified", ap_set_flag_slot_char,
+ (void *)APR_OFFSETOF(include_dir_config, lastmodified),
+ OR_LIMIT, "Whether to set the last modified header or respect "
+ "an existing header. Limited to 'on' or 'off'"),
+ AP_INIT_FLAG("SSIEtag", ap_set_flag_slot_char,
+ (void *)APR_OFFSETOF(include_dir_config, etag),
+ OR_LIMIT, "Whether to allow the generation of ETags within the server. "
+ "Existing ETags will be preserved. Limited to 'on' or 'off'"),
+ {NULL}
+};
+
+static void ap_register_include_handler(char *tag, include_handler_fn_t *func)
+{
+ apr_hash_set(include_handlers, tag, strlen(tag), (const void *)func);
+}
+
+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);
+}
+
+AP_DECLARE_MODULE(include) =
+{
+ STANDARD20_MODULE_STUFF,
+ create_includes_dir_config, /* dir config creater */
+ merge_includes_dir_config, /* dir config merger */
+ create_includes_server_config,/* server config */
+ NULL, /* merge server config */
+ includes_cmds, /* command apr_table_t */
+ register_hooks /* register hooks */
+};
diff --git a/modules/filters/mod_include.dep b/modules/filters/mod_include.dep
new file mode 100644
index 0000000..8e4e3e5
--- /dev/null
+++ b/modules/filters/mod_include.dep
@@ -0,0 +1,63 @@
+# Microsoft Developer Studio Generated Dependency File, included by mod_include.mak
+
+..\..\build\win32\httpd.rc : \
+ "..\..\include\ap_release.h"\
+
+
+.\mod_include.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_main.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_charset.h"\
+ "..\..\include\util_ebcdic.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\include\util_script.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apr_xlate.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\mod_include.h"\
+
diff --git a/modules/filters/mod_include.dsp b/modules/filters/mod_include.dsp
new file mode 100644
index 0000000..2b60f15
--- /dev/null
+++ b/modules/filters/mod_include.dsp
@@ -0,0 +1,115 @@
+# 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 /O2 /Oy- /Zi /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 /fo"Release/mod_include.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_include.so" /d LONG_NAME="include_module for Apache"
+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
+# Begin Special Build Tool
+TargetPath=.\Release\mod_include.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!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 /fo"Debug/mod_include.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_include.so" /d LONG_NAME="include_module for Apache"
+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
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_include.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!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=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
diff --git a/modules/filters/mod_include.exp b/modules/filters/mod_include.exp
new file mode 100644
index 0000000..112e1c4
--- /dev/null
+++ b/modules/filters/mod_include.exp
@@ -0,0 +1 @@
+include_module
diff --git a/modules/filters/mod_include.h b/modules/filters/mod_include.h
new file mode 100644
index 0000000..73714a2
--- /dev/null
+++ b/modules/filters/mod_include.h
@@ -0,0 +1,120 @@
+/* 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.
+ */
+
+/**
+ * @file mod_include.h
+ * @brief Server Side Include Filter Extension Module for Apache
+ *
+ * @defgroup MOD_INCLUDE mod_include
+ * @ingroup APACHE_MODS
+ * @{
+ */
+
+#ifndef _MOD_INCLUDE_H
+#define _MOD_INCLUDE_H 1
+
+#include "apr_pools.h"
+#include "apr_optional.h"
+
+/*
+ * Constants used for ap_ssi_get_tag_and_value's decode parameter
+ */
+#define SSI_VALUE_DECODED 1
+#define SSI_VALUE_RAW 0
+
+/*
+ * Constants used for ap_ssi_parse_string's leave_name parameter
+ */
+#define SSI_EXPAND_LEAVE_NAME 1
+#define SSI_EXPAND_DROP_NAME 0
+
+/*
+ * This macro creates a bucket which contains an error message and appends it
+ * to the current pass brigade
+ */
+#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))
+
+/*
+ * These constants are used to set or clear flag bits.
+ */
+#define SSI_FLAG_PRINTING (1<<0) /* Printing conditional lines. */
+#define SSI_FLAG_COND_TRUE (1<<1) /* Conditional eval'd to true. */
+#define SSI_FLAG_SIZE_IN_BYTES (1<<2) /* Sizes displayed in bytes. */
+#define SSI_FLAG_NO_EXEC (1<<3) /* No Exec in current context. */
+
+#define SSI_FLAG_SIZE_ABBREV (~(SSI_FLAG_SIZE_IN_BYTES))
+#define SSI_FLAG_CLEAR_PRINT_COND (~((SSI_FLAG_PRINTING) | \
+ (SSI_FLAG_COND_TRUE)))
+#define SSI_FLAG_CLEAR_PRINTING (~(SSI_FLAG_PRINTING))
+
+/*
+ * The public SSI context structure
+ */
+typedef struct {
+ /* permanent pool, use this for creating bucket data */
+ apr_pool_t *pool;
+
+ /* temp pool; will be cleared after the execution of every directive */
+ apr_pool_t *dpool;
+
+ /* See the SSI_FLAG_XXXXX definitions. */
+ int flags;
+
+ /* nesting of *invisible* ifs */
+ int if_nesting_level;
+
+ /* if true, the current buffer will be passed down the filter chain before
+ * continuing with next input bucket and the variable will be reset to
+ * false.
+ */
+ int flush_now;
+
+ /* argument counter (of the current directive) */
+ unsigned argc;
+
+ /* currently configured error string */
+ const char *error_str;
+
+ /* currently configured time format */
+ const char *time_str;
+
+ /* the current request */
+ request_rec *r;
+
+ /* pointer to internal (non-public) data, don't touch */
+ struct ssi_internal_ctx *intern;
+
+} include_ctx_t;
+
+typedef apr_status_t (include_handler_fn_t)(include_ctx_t *, ap_filter_t *,
+ apr_bucket_brigade *);
+
+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,
+ (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/modules/filters/mod_include.mak b/modules/filters/mod_include.mak
new file mode 100644
index 0000000..a9c3fed
--- /dev/null
+++ b/modules/filters/mod_include.mak
@@ -0,0 +1,353 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on mod_include.dsp
+!IF "$(CFG)" == ""
+CFG=mod_include - Win32 Release
+!MESSAGE No configuration specified. Defaulting to mod_include - Win32 Release.
+!ENDIF
+
+!IF "$(CFG)" != "mod_include - Win32 Release" && "$(CFG)" != "mod_include - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!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
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(CFG)" == "mod_include - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_include.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_include.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_include.obj"
+ -@erase "$(INTDIR)\mod_include.res"
+ -@erase "$(INTDIR)\mod_include_src.idb"
+ -@erase "$(INTDIR)\mod_include_src.pdb"
+ -@erase "$(OUTDIR)\mod_include.exp"
+ -@erase "$(OUTDIR)\mod_include.lib"
+ -@erase "$(OUTDIR)\mod_include.pdb"
+ -@erase "$(OUTDIR)\mod_include.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_include_src" /FD /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_include.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_include.so" /d LONG_NAME="include_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_include.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_include.pdb" /debug /out:"$(OUTDIR)\mod_include.so" /implib:"$(OUTDIR)\mod_include.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_include.so /opt:ref
+LINK32_OBJS= \
+ "$(INTDIR)\mod_include.obj" \
+ "$(INTDIR)\mod_include.res" \
+ "..\..\srclib\apr\Release\libapr-1.lib" \
+ "..\..\srclib\apr-util\Release\libaprutil-1.lib" \
+ "..\..\Release\libhttpd.lib"
+
+"$(OUTDIR)\mod_include.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Release\mod_include.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_include.so"
+ if exist .\Release\mod_include.so.manifest mt.exe -manifest .\Release\mod_include.so.manifest -outputresource:.\Release\mod_include.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ELSEIF "$(CFG)" == "mod_include - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_include.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_include.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_include.obj"
+ -@erase "$(INTDIR)\mod_include.res"
+ -@erase "$(INTDIR)\mod_include_src.idb"
+ -@erase "$(INTDIR)\mod_include_src.pdb"
+ -@erase "$(OUTDIR)\mod_include.exp"
+ -@erase "$(OUTDIR)\mod_include.lib"
+ -@erase "$(OUTDIR)\mod_include.pdb"
+ -@erase "$(OUTDIR)\mod_include.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_include_src" /FD /EHsc /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_include.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_include.so" /d LONG_NAME="include_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_include.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_include.pdb" /debug /out:"$(OUTDIR)\mod_include.so" /implib:"$(OUTDIR)\mod_include.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_include.so
+LINK32_OBJS= \
+ "$(INTDIR)\mod_include.obj" \
+ "$(INTDIR)\mod_include.res" \
+ "..\..\srclib\apr\Debug\libapr-1.lib" \
+ "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \
+ "..\..\Debug\libhttpd.lib"
+
+"$(OUTDIR)\mod_include.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Debug\mod_include.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_include.so"
+ if exist .\Debug\mod_include.so.manifest mt.exe -manifest .\Debug\mod_include.so.manifest -outputresource:.\Debug\mod_include.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+
+!IF "$(NO_EXTERNAL_DEPS)" != "1"
+!IF EXISTS("mod_include.dep")
+!INCLUDE "mod_include.dep"
+!ELSE
+!MESSAGE Warning: cannot find "mod_include.dep"
+!ENDIF
+!ENDIF
+
+
+!IF "$(CFG)" == "mod_include - Win32 Release" || "$(CFG)" == "mod_include - Win32 Debug"
+
+!IF "$(CFG)" == "mod_include - Win32 Release"
+
+"libapr - Win32 Release" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_include - Win32 Debug"
+
+"libapr - Win32 Debug" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_include - Win32 Release"
+
+"libaprutil - Win32 Release" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_include - Win32 Debug"
+
+"libaprutil - Win32 Debug" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_include - Win32 Release"
+
+"libhttpd - Win32 Release" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 ReleaseCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_include - Win32 Debug"
+
+"libhttpd - Win32 Debug" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 DebugCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ENDIF
+
+SOURCE=..\..\build\win32\httpd.rc
+
+!IF "$(CFG)" == "mod_include - Win32 Release"
+
+
+"$(INTDIR)\mod_include.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_include.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_include.so" /d LONG_NAME="include_module for Apache" $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "mod_include - Win32 Debug"
+
+
+"$(INTDIR)\mod_include.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_include.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_include.so" /d LONG_NAME="include_module for Apache" $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\mod_include.c
+
+"$(INTDIR)\mod_include.obj" : $(SOURCE) "$(INTDIR)"
+
+
+
+!ENDIF
+
diff --git a/modules/filters/mod_proxy_html.c b/modules/filters/mod_proxy_html.c
new file mode 100644
index 0000000..7783da1
--- /dev/null
+++ b/modules/filters/mod_proxy_html.c
@@ -0,0 +1,1353 @@
+/* Copyright (c) 2003-11, WebThing Ltd
+ * Copyright (c) 2011-, The Apache Software Foundation
+ *
+ * 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.
+ */
+
+/* GO_FASTER
+ You can #define GO_FASTER to disable trace logging.
+*/
+
+#ifdef GO_FASTER
+#define VERBOSE(x)
+#define VERBOSEB(x)
+#else
+#define VERBOSE(x) if (verbose) x
+#define VERBOSEB(x) if (verbose) {x}
+#endif
+
+/* libxml2 includes unicode/[...].h files which uses C++ comments */
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic warning "-Wcomment"
+#elif defined(__GNUC__)
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-Wcomment"
+#endif
+#endif
+
+/* libxml2 */
+#include <libxml/HTMLparser.h>
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#elif defined(__GNUC__)
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+#pragma GCC diagnostic pop
+#endif
+#endif
+
+#include "http_protocol.h"
+#include "http_config.h"
+#include "http_log.h"
+#include "apr_strings.h"
+#include "apr_hash.h"
+#include "apr_strmatch.h"
+#include "apr_lib.h"
+
+#include "apr_optional.h"
+#include "mod_xml2enc.h"
+#include "http_request.h"
+#include "ap_expr.h"
+
+/* globals set once at startup */
+static ap_rxplus_t *old_expr;
+static ap_regex_t *seek_meta;
+static const apr_strmatch_pattern* seek_content;
+static apr_status_t (*xml2enc_charset)(request_rec*, xmlCharEncoding*, const char**) = NULL;
+static apr_status_t (*xml2enc_filter)(request_rec*, const char*, unsigned int) = NULL;
+
+module AP_MODULE_DECLARE_DATA proxy_html_module;
+
+#define M_HTML 0x01
+#define M_EVENTS 0x02
+#define M_CDATA 0x04
+#define M_REGEX 0x08
+#define M_ATSTART 0x10
+#define M_ATEND 0x20
+#define M_LAST 0x40
+#define M_NOTLAST 0x80
+#define M_INTERPOLATE_TO 0x100
+#define M_INTERPOLATE_FROM 0x200
+
+typedef struct {
+ const char *val;
+} tattr;
+typedef struct {
+ unsigned int start;
+ unsigned int end;
+} meta;
+typedef struct urlmap {
+ struct urlmap *next;
+ unsigned int flags;
+ unsigned int regflags;
+ union {
+ const char *c;
+ ap_regex_t *r;
+ } from;
+ const char *to;
+ ap_expr_info_t *cond;
+} urlmap;
+typedef struct {
+ urlmap *map;
+ const char *doctype;
+ const char *etag;
+ unsigned int flags;
+ int bufsz;
+ apr_hash_t *links;
+ apr_array_header_t *events;
+ const char *charset_out;
+ int extfix;
+ int metafix;
+ int strip_comments;
+ int interp;
+ int enabled;
+} proxy_html_conf;
+typedef struct {
+ ap_filter_t *f;
+ proxy_html_conf *cfg;
+ htmlParserCtxtPtr parser;
+ apr_bucket_brigade *bb;
+ char *buf;
+ size_t offset;
+ size_t avail;
+ const char *encoding;
+ urlmap *map;
+ char rbuf[4];
+ apr_size_t rlen;
+ apr_size_t rmin;
+} saxctxt;
+
+
+#define NORM_LC 0x1
+#define NORM_MSSLASH 0x2
+#define NORM_RESET 0x4
+static htmlSAXHandler sax;
+
+typedef enum { ATTR_IGNORE, ATTR_URI, ATTR_EVENT } rewrite_t;
+
+static const char *const fpi_html =
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n";
+static const char *const fpi_html_legacy =
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n";
+static const char *const fpi_xhtml =
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
+static const char *const fpi_xhtml_legacy =
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
+static const char *const fpi_html5 = "<!DOCTYPE html>\n";
+static const char *const html_etag = ">";
+static const char *const xhtml_etag = " />";
+/*#define DEFAULT_DOCTYPE fpi_html */
+static const char *const DEFAULT_DOCTYPE = "";
+#define DEFAULT_ETAG html_etag
+
+static void normalise(unsigned int flags, char *str)
+{
+ char *p;
+ if (flags & NORM_LC)
+ for (p = str; *p; ++p)
+ if (isupper(*p))
+ *p = tolower(*p);
+
+ if (flags & NORM_MSSLASH)
+ for (p = ap_strchr(str, '\\'); p; p = ap_strchr(p+1, '\\'))
+ *p = '/';
+
+}
+#define consume_buffer(ctx,inbuf,bytes,flag) \
+ htmlParseChunk(ctx->parser, inbuf, bytes, flag)
+
+#define AP_fwrite(ctx,inbuf,bytes,flush) \
+ ap_fwrite(ctx->f->next, ctx->bb, inbuf, bytes);
+
+/* This is always utf-8 on entry. We can convert charset within FLUSH */
+#define FLUSH AP_fwrite(ctx, (chars+begin), (i-begin), 0); begin = i+1
+static void pcharacters(void *ctxt, const xmlChar *uchars, int length)
+{
+ const char *chars = (const char*) uchars;
+ saxctxt *ctx = (saxctxt*) ctxt;
+ int i;
+ int begin;
+ for (begin=i=0; i<length; i++) {
+ switch (chars[i]) {
+ case '&' : FLUSH; ap_fputs(ctx->f->next, ctx->bb, "&amp;"); break;
+ case '<' : FLUSH; ap_fputs(ctx->f->next, ctx->bb, "&lt;"); break;
+ case '>' : FLUSH; ap_fputs(ctx->f->next, ctx->bb, "&gt;"); break;
+ case '"' : FLUSH; ap_fputs(ctx->f->next, ctx->bb, "&quot;"); break;
+ default : break;
+ }
+ }
+ FLUSH;
+}
+
+static void preserve(saxctxt *ctx, const size_t len)
+{
+ char *newbuf;
+ if (len <= (ctx->avail - ctx->offset))
+ return;
+ else while (len > (ctx->avail - ctx->offset))
+ ctx->avail += ctx->cfg->bufsz;
+
+ newbuf = realloc(ctx->buf, ctx->avail);
+ if (newbuf != ctx->buf) {
+ if (ctx->buf)
+ apr_pool_cleanup_kill(ctx->f->r->pool, ctx->buf,
+ (int(*)(void*))free);
+ apr_pool_cleanup_register(ctx->f->r->pool, newbuf,
+ (int(*)(void*))free, apr_pool_cleanup_null);
+ ctx->buf = newbuf;
+ }
+}
+
+static void pappend(saxctxt *ctx, const char *buf, const size_t len)
+{
+ preserve(ctx, len);
+ memcpy(ctx->buf+ctx->offset, buf, len);
+ ctx->offset += len;
+}
+
+static void dump_content(saxctxt *ctx)
+{
+ urlmap *m;
+ char *found;
+ size_t s_from, s_to;
+ size_t match;
+ char c = 0;
+ int nmatch;
+ ap_regmatch_t pmatch[10];
+ char *subs;
+ size_t len, offs;
+ urlmap *themap = ctx->map;
+#ifndef GO_FASTER
+ int verbose = APLOGrtrace1(ctx->f->r);
+#endif
+
+ pappend(ctx, &c, 1); /* append null byte */
+ /* parse the text for URLs */
+ for (m = themap; m; m = m->next) {
+ if (!(m->flags & M_CDATA))
+ continue;
+ if (m->flags & M_REGEX) {
+ nmatch = 10;
+ offs = 0;
+ while (!ap_regexec(m->from.r, ctx->buf+offs, nmatch, pmatch, 0)) {
+ match = pmatch[0].rm_so;
+ s_from = pmatch[0].rm_eo - match;
+ subs = ap_pregsub(ctx->f->r->pool, m->to, ctx->buf+offs,
+ nmatch, pmatch);
+ s_to = strlen(subs);
+ len = strlen(ctx->buf);
+ offs += match;
+ VERBOSEB(
+ const char *f = apr_pstrndup(ctx->f->r->pool,
+ ctx->buf + offs, s_from);
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, ctx->f->r,
+ "C/RX: match at %s, substituting %s", f, subs);
+ )
+ if (s_to > s_from) {
+ preserve(ctx, s_to - s_from);
+ memmove(ctx->buf+offs+s_to, ctx->buf+offs+s_from,
+ len + 1 - s_from - offs);
+ memcpy(ctx->buf+offs, subs, s_to);
+ }
+ else {
+ memcpy(ctx->buf + offs, subs, s_to);
+ memmove(ctx->buf+offs+s_to, ctx->buf+offs+s_from,
+ len + 1 - s_from - offs);
+ }
+ offs += s_to;
+ }
+ }
+ else {
+ s_from = strlen(m->from.c);
+ s_to = strlen(m->to);
+ for (found = strstr(ctx->buf, m->from.c); found;
+ found = strstr(ctx->buf+match+s_to, m->from.c)) {
+ match = found - ctx->buf;
+ if ((m->flags & M_ATSTART) && (match != 0))
+ break;
+ len = strlen(ctx->buf);
+ if ((m->flags & M_ATEND) && (match < (len - s_from)))
+ continue;
+ VERBOSE(ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, ctx->f->r,
+ "C: matched %s, substituting %s",
+ m->from.c, m->to));
+ if (s_to > s_from) {
+ preserve(ctx, s_to - s_from);
+ memmove(ctx->buf+match+s_to, ctx->buf+match+s_from,
+ len + 1 - s_from - match);
+ memcpy(ctx->buf+match, m->to, s_to);
+ }
+ else {
+ memcpy(ctx->buf+match, m->to, s_to);
+ memmove(ctx->buf+match+s_to, ctx->buf+match+s_from,
+ len + 1 - s_from - match);
+ }
+ }
+ }
+ }
+ AP_fwrite(ctx, ctx->buf, strlen(ctx->buf), 1);
+}
+static void pcdata(void *ctxt, const xmlChar *uchars, int length)
+{
+ const char *chars = (const char*) uchars;
+ saxctxt *ctx = (saxctxt*) ctxt;
+ if (ctx->cfg->extfix) {
+ pappend(ctx, chars, length);
+ }
+ else {
+ /* not sure if this should force-flush
+ * (i.e. can one cdata section come in multiple calls?)
+ */
+ AP_fwrite(ctx, chars, length, 0);
+ }
+}
+static void pcomment(void *ctxt, const xmlChar *uchars)
+{
+ const char *chars = (const char*) uchars;
+ saxctxt *ctx = (saxctxt*) ctxt;
+ if (ctx->cfg->strip_comments)
+ return;
+
+ if (ctx->cfg->extfix) {
+ pappend(ctx, "<!--", 4);
+ pappend(ctx, chars, strlen(chars));
+ pappend(ctx, "-->", 3);
+ }
+ else {
+ ap_fputs(ctx->f->next, ctx->bb, "<!--");
+ AP_fwrite(ctx, chars, strlen(chars), 1);
+ ap_fputs(ctx->f->next, ctx->bb, "-->");
+ dump_content(ctx);
+ }
+}
+static void pendElement(void *ctxt, const xmlChar *uname)
+{
+ saxctxt *ctx = (saxctxt*) ctxt;
+ const char *name = (const char*) uname;
+ const htmlElemDesc* desc = htmlTagLookup(uname);
+
+ if ((ctx->cfg->doctype == fpi_html) || (ctx->cfg->doctype == fpi_xhtml)) {
+ /* enforce html */
+ if (!desc || desc->depr)
+ return;
+
+ }
+ else if ((ctx->cfg->doctype == fpi_html_legacy)
+ || (ctx->cfg->doctype == fpi_xhtml_legacy)) {
+ /* enforce html legacy */
+ if (!desc)
+ return;
+ }
+ /* TODO - implement HTML "allowed here" using the stack */
+ /* nah. Keeping the stack is too much overhead */
+
+ if (ctx->offset > 0) {
+ dump_content(ctx);
+ ctx->offset = 0; /* having dumped it, we can re-use the memory */
+ }
+ if (!desc || !desc->empty) {
+ ap_fprintf(ctx->f->next, ctx->bb, "</%s>", name);
+ }
+}
+
+static void pstartElement(void *ctxt, const xmlChar *uname,
+ const xmlChar** uattrs)
+{
+ int required_attrs;
+ int num_match;
+ size_t offs, len;
+ char *subs;
+ rewrite_t is_uri;
+ const char** a;
+ urlmap *m;
+ size_t s_to, s_from, match;
+ char *found;
+ saxctxt *ctx = (saxctxt*) ctxt;
+ size_t nmatch;
+ ap_regmatch_t pmatch[10];
+#ifndef GO_FASTER
+ int verbose = APLOGrtrace1(ctx->f->r);
+#endif
+ apr_array_header_t *linkattrs;
+ int i;
+ const char *name = (const char*) uname;
+ const char** attrs = (const char**) uattrs;
+ const htmlElemDesc* desc = htmlTagLookup(uname);
+ urlmap *themap = ctx->map;
+#ifdef HAVE_STACK
+ const void** descp;
+#endif
+ int enforce = 0;
+ if ((ctx->cfg->doctype == fpi_html) || (ctx->cfg->doctype == fpi_xhtml)) {
+ /* enforce html */
+ if (!desc || desc->depr) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->f->r, APLOGNO(01416)
+ "Bogus HTML element %s dropped", name);
+ return;
+ }
+ enforce = 2;
+ }
+ else if ((ctx->cfg->doctype == fpi_html_legacy)
+ || (ctx->cfg->doctype == fpi_xhtml_legacy)) {
+ /* enforce html legacy */
+ if (!desc) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->f->r, APLOGNO(01417)
+ "Deprecated HTML element %s dropped", name);
+ return;
+ }
+ enforce = 1;
+ }
+#ifdef HAVE_STACK
+ descp = apr_array_push(ctx->stack);
+ *descp = desc;
+ /* TODO - implement HTML "allowed here" */
+#endif
+
+ ap_fputc(ctx->f->next, ctx->bb, '<');
+ ap_fputs(ctx->f->next, ctx->bb, name);
+
+ required_attrs = 0;
+ if ((enforce > 0) && (desc != NULL) && (desc->attrs_req != NULL))
+ for (a = desc->attrs_req; *a; a++)
+ ++required_attrs;
+
+ if (attrs) {
+ linkattrs = apr_hash_get(ctx->cfg->links, name, APR_HASH_KEY_STRING);
+ for (a = attrs; *a; a += 2) {
+ if (desc && enforce > 0) {
+ switch (htmlAttrAllowed(desc, (xmlChar*)*a, 2-enforce)) {
+ case HTML_INVALID:
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->f->r, APLOGNO(01418)
+ "Bogus HTML attribute %s of %s dropped",
+ *a, name);
+ continue;
+ case HTML_DEPRECATED:
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->f->r, APLOGNO(01419)
+ "Deprecated HTML attribute %s of %s dropped",
+ *a, name);
+ continue;
+ case HTML_REQUIRED:
+ required_attrs--; /* cross off the number still needed */
+ /* fallthrough - required implies valid */
+ default:
+ break;
+ }
+ }
+ ctx->offset = 0;
+ if (a[1]) {
+ pappend(ctx, a[1], strlen(a[1])+1);
+ is_uri = ATTR_IGNORE;
+ if (linkattrs) {
+ tattr *attrs = (tattr*) linkattrs->elts;
+ for (i=0; i < linkattrs->nelts; ++i) {
+ if (!strcmp(*a, attrs[i].val)) {
+ is_uri = ATTR_URI;
+ break;
+ }
+ }
+ }
+ if ((is_uri == ATTR_IGNORE) && ctx->cfg->extfix
+ && (ctx->cfg->events != NULL)) {
+ for (i=0; i < ctx->cfg->events->nelts; ++i) {
+ tattr *attrs = (tattr*) ctx->cfg->events->elts;
+ if (!strcmp(*a, attrs[i].val)) {
+ is_uri = ATTR_EVENT;
+ break;
+ }
+ }
+ }
+ switch (is_uri) {
+ case ATTR_URI:
+ num_match = 0;
+ for (m = themap; m; m = m->next) {
+ if (!(m->flags & M_HTML))
+ continue;
+ if (m->flags & M_REGEX) {
+ nmatch = 10;
+ if (!ap_regexec(m->from.r, ctx->buf, nmatch,
+ pmatch, 0)) {
+ ++num_match;
+ offs = match = pmatch[0].rm_so;
+ s_from = pmatch[0].rm_eo - match;
+ subs = ap_pregsub(ctx->f->r->pool, m->to,
+ ctx->buf, nmatch, pmatch);
+ VERBOSE({
+ const char *f;
+ f = apr_pstrndup(ctx->f->r->pool,
+ ctx->buf + offs, s_from);
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0,
+ ctx->f->r,
+ "H/RX: match at %s, substituting %s",
+ f, subs);
+ })
+ s_to = strlen(subs);
+ len = strlen(ctx->buf);
+ if (s_to > s_from) {
+ preserve(ctx, s_to - s_from);
+ memmove(ctx->buf+offs+s_to,
+ ctx->buf+offs+s_from,
+ len + 1 - s_from - offs);
+ memcpy(ctx->buf+offs, subs, s_to);
+ }
+ else {
+ memcpy(ctx->buf + offs, subs, s_to);
+ memmove(ctx->buf+offs+s_to,
+ ctx->buf+offs+s_from,
+ len + 1 - s_from - offs);
+ }
+ }
+ } else {
+ s_from = strlen(m->from.c);
+ if (!strncasecmp(ctx->buf, m->from.c, s_from)) {
+ ++num_match;
+ s_to = strlen(m->to);
+ len = strlen(ctx->buf);
+ VERBOSE(ap_log_rerror(APLOG_MARK, APLOG_TRACE3,
+ 0, ctx->f->r,
+ "H: matched %s, substituting %s",
+ m->from.c, m->to));
+ if (s_to > s_from) {
+ preserve(ctx, s_to - s_from);
+ memmove(ctx->buf+s_to, ctx->buf+s_from,
+ len + 1 - s_from);
+ memcpy(ctx->buf, m->to, s_to);
+ }
+ else { /* it fits in the existing space */
+ memcpy(ctx->buf, m->to, s_to);
+ memmove(ctx->buf+s_to, ctx->buf+s_from,
+ len + 1 - s_from);
+ }
+ break;
+ }
+ }
+ /* URIs only want one match unless overridden in the config */
+ if ((num_match > 0) && !(m->flags & M_NOTLAST))
+ break;
+ }
+ break;
+ case ATTR_EVENT:
+ for (m = themap; m; m = m->next) {
+ num_match = 0; /* reset here since we're working per-rule */
+ if (!(m->flags & M_EVENTS))
+ continue;
+ if (m->flags & M_REGEX) {
+ nmatch = 10;
+ offs = 0;
+ while (!ap_regexec(m->from.r, ctx->buf+offs,
+ nmatch, pmatch, 0)) {
+ match = pmatch[0].rm_so;
+ s_from = pmatch[0].rm_eo - match;
+ subs = ap_pregsub(ctx->f->r->pool, m->to, ctx->buf+offs,
+ nmatch, pmatch);
+ VERBOSE({
+ const char *f;
+ f = apr_pstrndup(ctx->f->r->pool,
+ ctx->buf + offs, s_from);
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0,
+ ctx->f->r,
+ "E/RX: match at %s, substituting %s",
+ f, subs);
+ })
+ s_to = strlen(subs);
+ offs += match;
+ len = strlen(ctx->buf);
+ if (s_to > s_from) {
+ preserve(ctx, s_to - s_from);
+ memmove(ctx->buf+offs+s_to,
+ ctx->buf+offs+s_from,
+ len + 1 - s_from - offs);
+ memcpy(ctx->buf+offs, subs, s_to);
+ }
+ else {
+ memcpy(ctx->buf + offs, subs, s_to);
+ memmove(ctx->buf+offs+s_to,
+ ctx->buf+offs+s_from,
+ len + 1 - s_from - offs);
+ }
+ offs += s_to;
+ ++num_match;
+ }
+ }
+ else {
+ found = strstr(ctx->buf, m->from.c);
+ if ((m->flags & M_ATSTART) && (found != ctx->buf))
+ continue;
+ while (found) {
+ s_from = strlen(m->from.c);
+ s_to = strlen(m->to);
+ match = found - ctx->buf;
+ if ((s_from < strlen(found))
+ && (m->flags & M_ATEND)) {
+ found = strstr(ctx->buf+match+s_from,
+ m->from.c);
+ continue;
+ }
+ else {
+ found = strstr(ctx->buf+match+s_to,
+ m->from.c);
+ }
+ VERBOSE(ap_log_rerror(APLOG_MARK, APLOG_TRACE3,
+ 0, ctx->f->r,
+ "E: matched %s, substituting %s",
+ m->from.c, m->to));
+ len = strlen(ctx->buf);
+ if (s_to > s_from) {
+ preserve(ctx, s_to - s_from);
+ memmove(ctx->buf+match+s_to,
+ ctx->buf+match+s_from,
+ len + 1 - s_from - match);
+ memcpy(ctx->buf+match, m->to, s_to);
+ }
+ else {
+ memcpy(ctx->buf+match, m->to, s_to);
+ memmove(ctx->buf+match+s_to,
+ ctx->buf+match+s_from,
+ len + 1 - s_from - match);
+ }
+ ++num_match;
+ }
+ }
+ if (num_match && (m->flags & M_LAST))
+ break;
+ }
+ break;
+ case ATTR_IGNORE:
+ break;
+ }
+ }
+ if (!a[1])
+ ap_fputstrs(ctx->f->next, ctx->bb, " ", a[0], NULL);
+ else {
+
+ if (ctx->cfg->flags != 0)
+ normalise(ctx->cfg->flags, ctx->buf);
+
+ /* write the attribute, using pcharacters to html-escape
+ anything that needs it in the value.
+ */
+ ap_fputstrs(ctx->f->next, ctx->bb, " ", a[0], "=\"", NULL);
+ pcharacters(ctx, (const xmlChar*)ctx->buf, strlen(ctx->buf));
+ ap_fputc(ctx->f->next, ctx->bb, '"');
+ }
+ }
+ }
+ ctx->offset = 0;
+ if (desc && desc->empty)
+ ap_fputs(ctx->f->next, ctx->bb, ctx->cfg->etag);
+ else
+ ap_fputc(ctx->f->next, ctx->bb, '>');
+
+ if ((enforce > 0) && (required_attrs > 0)) {
+ /* if there are more required attributes than we found then complain */
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->f->r, APLOGNO(01420)
+ "HTML element %s is missing %d required attributes",
+ name, required_attrs);
+ }
+}
+
+static meta *metafix(request_rec *r, const char *buf, apr_size_t len)
+{
+ meta *ret = NULL;
+ size_t offs = 0;
+ const char *p;
+ const char *q;
+ char *header;
+ char *content;
+ ap_regmatch_t pmatch[2];
+ char delim;
+
+ while (offs < len &&
+ !ap_regexec_len(seek_meta, buf + offs, len - offs, 2, pmatch, 0)) {
+ header = NULL;
+ content = NULL;
+ p = buf+offs+pmatch[1].rm_eo;
+ while (!apr_isalpha(*++p));
+ for (q = p; apr_isalnum(*q) || (*q == '-'); ++q);
+ header = apr_pstrmemdup(r->pool, p, q-p);
+ if (ap_cstr_casecmpn(header, "Content-", 8)) {
+ /* find content=... string */
+ p = apr_strmatch(seek_content, buf+offs+pmatch[0].rm_so,
+ pmatch[0].rm_eo - pmatch[0].rm_so);
+ /* if it doesn't contain "content", ignore, don't crash! */
+ if (p != NULL) {
+ while (*p) {
+ p += 7;
+ while (apr_isspace(*p))
+ ++p;
+ /* XXX Should we search for another content= pattern? */
+ if (*p != '=')
+ break;
+ while (*p && apr_isspace(*++p));
+ if ((*p == '\'') || (*p == '"')) {
+ delim = *p++;
+ for (q = p; *q && *q != delim; ++q);
+ /* No terminating delimiter found? Skip the bogus directive */
+ if (*q != delim)
+ break;
+ } else {
+ for (q = p; *q && !apr_isspace(*q) && (*q != '>'); ++q);
+ }
+ content = apr_pstrmemdup(r->pool, p, q-p);
+ break;
+ }
+ }
+ }
+ else if (!ap_cstr_casecmpn(header, "Content-Type", 12)) {
+ ret = apr_palloc(r->pool, sizeof(meta));
+ ret->start = offs+pmatch[0].rm_so;
+ ret->end = offs+pmatch[0].rm_eo;
+ }
+ if (header && content) {
+#ifndef GO_FASTER
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
+ "Adding header [%s: %s] from HTML META",
+ header, content);
+#endif
+ apr_table_setn(r->headers_out, header, content);
+ }
+ offs += pmatch[0].rm_eo;
+ }
+ return ret;
+}
+
+static const char *interpolate_vars(request_rec *r, const char *str)
+{
+ const char *start;
+ const char *end;
+ const char *delim;
+ const char *before;
+ const char *after;
+ const char *replacement;
+ const char *var;
+ for (;;) {
+ if ((start = ap_strstr_c(str, "${")) == NULL)
+ break;
+
+ if ((end = ap_strchr_c(start+2, '}')) == NULL)
+ break;
+
+ delim = ap_strchr_c(start+2, '|');
+
+ /* Restrict delim to ${...} */
+ if (delim && delim >= end) {
+ delim = NULL;
+ }
+
+ before = apr_pstrmemdup(r->pool, str, start-str);
+ after = end+1;
+ if (delim) {
+ var = apr_pstrmemdup(r->pool, start+2, delim-start-2);
+ }
+ else {
+ var = apr_pstrmemdup(r->pool, start+2, end-start-2);
+ }
+ replacement = apr_table_get(r->subprocess_env, var);
+ if (!replacement) {
+ if (delim)
+ replacement = apr_pstrmemdup(r->pool, delim+1, end-delim-1);
+ else
+ replacement = "";
+ }
+ str = apr_pstrcat(r->pool, before, replacement, after, NULL);
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+ "Interpolating %s => %s", var, replacement);
+ }
+ return str;
+}
+static void fixup_rules(saxctxt *ctx)
+{
+ urlmap *newp;
+ urlmap *p;
+ urlmap *prev = NULL;
+ request_rec *r = ctx->f->r;
+
+ for (p = ctx->cfg->map; p; p = p->next) {
+ if (p->cond != NULL) {
+ const char *err;
+ int ok = ap_expr_exec(r, p->cond, &err);
+ if (err) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01421)
+ "Error evaluating expr: %s", err);
+ }
+ if (ok == 0) {
+ continue; /* condition is unsatisfied */
+ }
+ }
+
+ newp = apr_pmemdup(r->pool, p, sizeof(urlmap));
+
+ if (newp->flags & M_INTERPOLATE_FROM) {
+ newp->from.c = interpolate_vars(r, newp->from.c);
+ if (!newp->from.c || !*newp->from.c)
+ continue; /* don't use empty from-pattern */
+ if (newp->flags & M_REGEX) {
+ newp->from.r = ap_pregcomp(r->pool, newp->from.c,
+ newp->regflags);
+ }
+ }
+ if (newp->flags & M_INTERPOLATE_TO) {
+ newp->to = interpolate_vars(r, newp->to);
+ }
+ /* evaluate p->cond; continue if unsatisfied */
+ /* create new urlmap with memcpy and append to map */
+ /* interpolate from if flagged to do so */
+ /* interpolate to if flagged to do so */
+
+ if (prev != NULL)
+ prev->next = newp;
+ else
+ ctx->map = newp;
+ prev = newp;
+ }
+
+ if (prev)
+ prev->next = NULL;
+}
+
+static saxctxt *check_filter_init (ap_filter_t *f)
+{
+ saxctxt *fctx;
+ if (!f->ctx) {
+ proxy_html_conf *cfg;
+ const char *force;
+ const char *errmsg = NULL;
+ cfg = ap_get_module_config(f->r->per_dir_config, &proxy_html_module);
+ force = apr_table_get(f->r->subprocess_env, "PROXY_HTML_FORCE");
+
+ if (!force) {
+ if (!f->r->proxyreq) {
+ errmsg = "Non-proxy request; not inserting proxy-html filter";
+ }
+ else if (!f->r->content_type) {
+ errmsg = "No content-type; bailing out of proxy-html filter";
+ }
+ else if (ap_cstr_casecmpn(f->r->content_type, "text/html", 9) &&
+ ap_cstr_casecmpn(f->r->content_type,
+ "application/xhtml+xml", 21)) {
+ errmsg = "Non-HTML content; not inserting proxy-html filter";
+ }
+ }
+ if (!cfg->links) {
+ errmsg = "No links configured: nothing for proxy-html filter to do";
+ }
+
+ if (errmsg) {
+#ifndef GO_FASTER
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, f->r, "%s", errmsg);
+#endif
+ ap_remove_output_filter(f);
+ return NULL;
+ }
+
+ fctx = f->ctx = apr_pcalloc(f->r->pool, sizeof(saxctxt));
+ fctx->f = f;
+ fctx->bb = apr_brigade_create(f->r->pool,
+ f->r->connection->bucket_alloc);
+ fctx->cfg = cfg;
+ apr_table_unset(f->r->headers_out, "Content-Length");
+
+ if (cfg->interp)
+ fixup_rules(fctx);
+ else
+ fctx->map = cfg->map;
+ /* defer dealing with charset_out until after sniffing charset_in
+ * so we can support setting one to t'other.
+ */
+ }
+ return f->ctx;
+}
+
+static void prepend_rbuf(saxctxt *ctxt, apr_bucket_brigade *bb)
+{
+ if (ctxt->rlen) {
+ apr_bucket *b = apr_bucket_transient_create(ctxt->rbuf,
+ ctxt->rlen,
+ bb->bucket_alloc);
+ APR_BRIGADE_INSERT_HEAD(bb, b);
+ ctxt->rlen = 0;
+ }
+}
+
+static apr_status_t proxy_html_filter(ap_filter_t *f, apr_bucket_brigade *bb)
+{
+ apr_bucket* b;
+ meta *m = NULL;
+ xmlCharEncoding enc;
+ const char *buf = 0;
+ apr_size_t bytes = 0;
+#ifndef USE_OLD_LIBXML2
+ int xmlopts = XML_PARSE_RECOVER | XML_PARSE_NONET |
+ XML_PARSE_NOBLANKS | XML_PARSE_NOERROR | XML_PARSE_NOWARNING;
+#endif
+
+ saxctxt *ctxt = check_filter_init(f);
+ if (!ctxt)
+ return ap_pass_brigade(f->next, bb);
+ for (b = APR_BRIGADE_FIRST(bb);
+ b != APR_BRIGADE_SENTINEL(bb);
+ b = APR_BUCKET_NEXT(b)) {
+ if (APR_BUCKET_IS_METADATA(b)) {
+ if (APR_BUCKET_IS_EOS(b)) {
+ if (ctxt->parser != NULL) {
+ consume_buffer(ctxt, "", 0, 1);
+ }
+ else {
+ prepend_rbuf(ctxt, ctxt->bb);
+ }
+ APR_BRIGADE_INSERT_TAIL(ctxt->bb,
+ apr_bucket_eos_create(ctxt->bb->bucket_alloc));
+ ap_pass_brigade(ctxt->f->next, ctxt->bb);
+ apr_brigade_cleanup(ctxt->bb);
+ }
+ else if (APR_BUCKET_IS_FLUSH(b)) {
+ /* pass on flush, except at start where it would cause
+ * headers to be sent before doc sniffing
+ */
+ if (ctxt->parser != NULL) {
+ ap_fflush(ctxt->f->next, ctxt->bb);
+ }
+ }
+ }
+ else if (apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ)
+ == APR_SUCCESS) {
+ if (ctxt->parser == NULL) {
+ const char *cenc;
+
+ /* For documents smaller than four bytes, there is no reason to do
+ * HTML rewriting. The URL schema (i.e. 'http') needs four bytes alone.
+ * And the HTML parser needs at least four bytes to initialise correctly.
+ */
+ ctxt->rmin += bytes;
+ if (ctxt->rmin < sizeof(ctxt->rbuf)) {
+ memcpy(ctxt->rbuf + ctxt->rlen, buf, bytes);
+ ctxt->rlen += bytes;
+ continue;
+ }
+ if (ctxt->rlen && ctxt->rlen < sizeof(ctxt->rbuf)) {
+ apr_size_t rem = sizeof(ctxt->rbuf) - ctxt->rlen;
+ memcpy(ctxt->rbuf + ctxt->rlen, buf, rem);
+ ctxt->rlen += rem;
+ buf += rem;
+ bytes -= rem;
+ }
+
+ if (!xml2enc_charset ||
+ (xml2enc_charset(f->r, &enc, &cenc) != APR_SUCCESS)) {
+ if (!xml2enc_charset)
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, f->r, APLOGNO(01422)
+ "No i18n support found. Install mod_xml2enc if required");
+ enc = XML_CHAR_ENCODING_NONE;
+ ap_set_content_type(f->r, "text/html;charset=utf-8");
+ }
+ else {
+ /* if we wanted a non-default charset_out, insert the
+ * xml2enc filter now that we've sniffed it
+ */
+ if (ctxt->cfg->charset_out && xml2enc_filter) {
+ if (*ctxt->cfg->charset_out != '*')
+ cenc = ctxt->cfg->charset_out;
+ xml2enc_filter(f->r, cenc, ENCIO_OUTPUT);
+ ap_set_content_type(f->r,
+ apr_pstrcat(f->r->pool,
+ "text/html;charset=",
+ cenc, NULL));
+ }
+ else /* Normal case, everything worked, utf-8 output */
+ ap_set_content_type(f->r, "text/html;charset=utf-8");
+ }
+
+ ap_fputs(f->next, ctxt->bb, ctxt->cfg->doctype);
+
+ if (ctxt->rlen) {
+ ctxt->parser = htmlCreatePushParserCtxt(&sax, ctxt,
+ ctxt->rbuf,
+ ctxt->rlen,
+ NULL, enc);
+ }
+ else {
+ ctxt->parser = htmlCreatePushParserCtxt(&sax, ctxt, buf, 4,
+ NULL, enc);
+ buf += 4;
+ bytes -= 4;
+ }
+ if (ctxt->parser == NULL) {
+ prepend_rbuf(ctxt, bb);
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+ ctxt->rlen = 0;
+ apr_pool_cleanup_register(f->r->pool, ctxt->parser,
+ (int(*)(void*))htmlFreeParserCtxt,
+ apr_pool_cleanup_null);
+#ifndef USE_OLD_LIBXML2
+ if (xmlopts = xmlCtxtUseOptions(ctxt->parser, xmlopts), xmlopts)
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, f->r, APLOGNO(01423)
+ "Unsupported parser opts %x", xmlopts);
+#endif
+ if (ctxt->cfg->metafix)
+ m = metafix(f->r, buf, bytes);
+ if (m) {
+ consume_buffer(ctxt, buf, m->start, 0);
+ consume_buffer(ctxt, buf+m->end, bytes-m->end, 0);
+ }
+ else {
+ consume_buffer(ctxt, buf, bytes, 0);
+ }
+ }
+ else {
+ consume_buffer(ctxt, buf, bytes, 0);
+ }
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01424)
+ "Error in bucket read");
+ }
+ }
+ /*ap_fflush(ctxt->f->next, ctxt->bb); // uncomment for debug */
+ apr_brigade_cleanup(bb);
+ return APR_SUCCESS;
+}
+
+static void *proxy_html_config(apr_pool_t *pool, char *x)
+{
+ proxy_html_conf *ret = apr_pcalloc(pool, sizeof(proxy_html_conf));
+ ret->doctype = DEFAULT_DOCTYPE;
+ ret->etag = DEFAULT_ETAG;
+ ret->bufsz = 8192;
+ /* ret->interp = 1; */
+ /* don't initialise links and events until they get set/used */
+ return ret;
+}
+
+static void *proxy_html_merge(apr_pool_t *pool, void *BASE, void *ADD)
+{
+ proxy_html_conf *base = (proxy_html_conf *) BASE;
+ proxy_html_conf *add = (proxy_html_conf *) ADD;
+ proxy_html_conf *conf = apr_palloc(pool, sizeof(proxy_html_conf));
+
+ /* don't merge declarations - just use the most specific */
+ conf->links = (add->links == NULL) ? base->links : add->links;
+ conf->events = (add->events == NULL) ? base->events : add->events;
+
+ conf->charset_out = (add->charset_out == NULL)
+ ? base->charset_out : add->charset_out;
+
+ if (add->map && base->map) {
+ urlmap *a;
+ conf->map = NULL;
+ for (a = base->map; a; a = a->next) {
+ urlmap *save = conf->map;
+ conf->map = apr_pmemdup(pool, a, sizeof(urlmap));
+ conf->map->next = save;
+ }
+ for (a = add->map; a; a = a->next) {
+ urlmap *save = conf->map;
+ conf->map = apr_pmemdup(pool, a, sizeof(urlmap));
+ conf->map->next = save;
+ }
+ }
+ else
+ conf->map = add->map ? add->map : base->map;
+
+ conf->doctype = (add->doctype == DEFAULT_DOCTYPE)
+ ? base->doctype : add->doctype;
+ conf->etag = (add->etag == DEFAULT_ETAG) ? base->etag : add->etag;
+ conf->bufsz = add->bufsz;
+ if (add->flags & NORM_RESET) {
+ conf->flags = add->flags ^ NORM_RESET;
+ conf->metafix = add->metafix;
+ conf->extfix = add->extfix;
+ conf->interp = add->interp;
+ conf->strip_comments = add->strip_comments;
+ conf->enabled = add->enabled;
+ }
+ else {
+ conf->flags = base->flags | add->flags;
+ conf->metafix = base->metafix | add->metafix;
+ conf->extfix = base->extfix | add->extfix;
+ conf->interp = base->interp | add->interp;
+ conf->strip_comments = base->strip_comments | add->strip_comments;
+ conf->enabled = add->enabled | base->enabled;
+ }
+ return conf;
+}
+#define REGFLAG(n,s,c) ((s&&(ap_strchr_c((s),(c))!=NULL)) ? (n) : 0)
+#define XREGFLAG(n,s,c) ((!s||(ap_strchr_c((s),(c))==NULL)) ? (n) : 0)
+static const char *comp_urlmap(cmd_parms *cmd, urlmap *newmap,
+ const char *from, const char *to,
+ const char *flags, const char *cond)
+{
+ const char *err = NULL;
+ newmap->flags
+ = XREGFLAG(M_HTML,flags,'h')
+ | XREGFLAG(M_EVENTS,flags,'e')
+ | XREGFLAG(M_CDATA,flags,'c')
+ | REGFLAG(M_ATSTART,flags,'^')
+ | REGFLAG(M_ATEND,flags,'$')
+ | REGFLAG(M_REGEX,flags,'R')
+ | REGFLAG(M_LAST,flags,'L')
+ | REGFLAG(M_NOTLAST,flags,'l')
+ | REGFLAG(M_INTERPOLATE_TO,flags,'V')
+ | REGFLAG(M_INTERPOLATE_FROM,flags,'v');
+
+ if ((newmap->flags & M_INTERPOLATE_FROM) || !(newmap->flags & M_REGEX)) {
+ newmap->from.c = from;
+ newmap->to = to;
+ }
+ else {
+ newmap->regflags
+ = REGFLAG(AP_REG_EXTENDED,flags,'x')
+ | REGFLAG(AP_REG_ICASE,flags,'i')
+ | REGFLAG(AP_REG_NOSUB,flags,'n')
+ | REGFLAG(AP_REG_NEWLINE,flags,'s');
+ newmap->from.r = ap_pregcomp(cmd->pool, from, newmap->regflags);
+ newmap->to = to;
+ }
+ if (cond != NULL) {
+ /* back-compatibility: support old-style ENV expressions
+ * by converting to ap_expr syntax.
+ *
+ * 1. var --> env(var)
+ * 2. var=val --> env(var)=val
+ * 3. !var --> !env(var)
+ * 4. !var=val --> env(var)!=val
+ */
+ char *newcond = NULL;
+ if (ap_rxplus_exec(cmd->temp_pool, old_expr, cond, &newcond)) {
+ /* we got a substitution. Check for the case (3) above
+ * that the regexp gets wrong: a negation without a comparison.
+ */
+ if ((cond[0] == '!') && !ap_strchr_c(cond, '=')) {
+ memmove(newcond+1, newcond, strlen(newcond)-1);
+ newcond[0] = '!';
+ }
+ cond = newcond;
+ }
+ newmap->cond = ap_expr_parse_cmd(cmd, cond, 0, &err, NULL);
+ }
+ else {
+ newmap->cond = NULL;
+ }
+ return err;
+}
+
+static const char *set_urlmap(cmd_parms *cmd, void *CFG, const char *args)
+{
+ proxy_html_conf *cfg = (proxy_html_conf *)CFG;
+ urlmap *map;
+ apr_pool_t *pool = cmd->pool;
+ urlmap *newmap;
+ const char *usage =
+ "Usage: ProxyHTMLURLMap from-pattern to-pattern [flags] [cond]";
+ const char *from;
+ const char *to;
+ const char *flags;
+ const char *cond = NULL;
+
+ if (from = ap_getword_conf(cmd->pool, &args), !from)
+ return usage;
+ if (to = ap_getword_conf(cmd->pool, &args), !to)
+ return usage;
+ flags = ap_getword_conf(cmd->pool, &args);
+ if (flags && *flags)
+ cond = ap_getword_conf(cmd->pool, &args);
+ if (cond && !*cond)
+ cond = NULL;
+
+ /* the args look OK, so let's use them */
+ newmap = apr_palloc(pool, sizeof(urlmap));
+ newmap->next = NULL;
+ if (cfg->map) {
+ for (map = cfg->map; map->next; map = map->next);
+ map->next = newmap;
+ }
+ else
+ cfg->map = newmap;
+
+ return comp_urlmap(cmd, newmap, from, to, flags, cond);
+}
+
+static const char *set_doctype(cmd_parms *cmd, void *CFG,
+ const char *t, const char *l)
+{
+ proxy_html_conf *cfg = (proxy_html_conf *)CFG;
+ if (!strcasecmp(t, "xhtml")) {
+ cfg->etag = xhtml_etag;
+ if (l && !strcasecmp(l, "legacy"))
+ cfg->doctype = fpi_xhtml_legacy;
+ else
+ cfg->doctype = fpi_xhtml;
+ }
+ else if (!strcasecmp(t, "html")) {
+ cfg->etag = html_etag;
+ if (l && !strcasecmp(l, "legacy"))
+ cfg->doctype = fpi_html_legacy;
+ else
+ cfg->doctype = fpi_html;
+ }
+ else if (!strcasecmp(t, "html5")) {
+ cfg->etag = html_etag;
+ cfg->doctype = fpi_html5;
+ }
+ else {
+ cfg->doctype = t;
+ if (l && ((l[0] == 'x') || (l[0] == 'X')))
+ cfg->etag = xhtml_etag;
+ else
+ cfg->etag = html_etag;
+ }
+ return NULL;
+}
+
+static const char *set_flags(cmd_parms *cmd, void *CFG, const char *arg)
+{
+ proxy_html_conf *cfg = CFG;
+ if (arg && *arg) {
+ if (!strcasecmp(arg, "lowercase"))
+ cfg->flags |= NORM_LC;
+ else if (!strcasecmp(arg, "dospath"))
+ cfg->flags |= NORM_MSSLASH;
+ else if (!strcasecmp(arg, "reset"))
+ cfg->flags |= NORM_RESET;
+ }
+ return NULL;
+}
+
+static const char *set_events(cmd_parms *cmd, void *CFG, const char *arg)
+{
+ tattr *attr;
+ proxy_html_conf *cfg = CFG;
+ if (cfg->events == NULL)
+ cfg->events = apr_array_make(cmd->pool, 20, sizeof(tattr));
+ attr = apr_array_push(cfg->events);
+ attr->val = arg;
+ return NULL;
+}
+
+static const char *set_links(cmd_parms *cmd, void *CFG,
+ const char *elt, const char *att)
+{
+ apr_array_header_t *attrs;
+ tattr *attr;
+ proxy_html_conf *cfg = CFG;
+
+ if (cfg->links == NULL)
+ cfg->links = apr_hash_make(cmd->pool);
+
+ attrs = apr_hash_get(cfg->links, elt, APR_HASH_KEY_STRING);
+ if (!attrs) {
+ attrs = apr_array_make(cmd->pool, 2, sizeof(tattr*));
+ apr_hash_set(cfg->links, elt, APR_HASH_KEY_STRING, attrs);
+ }
+ attr = apr_array_push(attrs);
+ attr->val = att;
+ return NULL;
+}
+static const command_rec proxy_html_cmds[] = {
+ AP_INIT_ITERATE("ProxyHTMLEvents", set_events, NULL,
+ RSRC_CONF|ACCESS_CONF,
+ "Strings to be treated as scripting events"),
+ AP_INIT_ITERATE2("ProxyHTMLLinks", set_links, NULL,
+ RSRC_CONF|ACCESS_CONF, "Declare HTML Attributes"),
+ AP_INIT_RAW_ARGS("ProxyHTMLURLMap", set_urlmap, NULL,
+ RSRC_CONF|ACCESS_CONF, "Map URL From To"),
+ AP_INIT_TAKE12("ProxyHTMLDoctype", set_doctype, NULL,
+ RSRC_CONF|ACCESS_CONF, "(HTML|XHTML) [Legacy]"),
+ AP_INIT_ITERATE("ProxyHTMLFixups", set_flags, NULL,
+ RSRC_CONF|ACCESS_CONF, "Options are lowercase, dospath"),
+ AP_INIT_FLAG("ProxyHTMLMeta", ap_set_flag_slot,
+ (void*)APR_OFFSETOF(proxy_html_conf, metafix),
+ RSRC_CONF|ACCESS_CONF, "Fix META http-equiv elements"),
+ AP_INIT_FLAG("ProxyHTMLInterp", ap_set_flag_slot,
+ (void*)APR_OFFSETOF(proxy_html_conf, interp),
+ RSRC_CONF|ACCESS_CONF,
+ "Support interpolation and conditions in URLMaps"),
+ AP_INIT_FLAG("ProxyHTMLExtended", ap_set_flag_slot,
+ (void*)APR_OFFSETOF(proxy_html_conf, extfix),
+ RSRC_CONF|ACCESS_CONF, "Map URLs in Javascript and CSS"),
+ AP_INIT_FLAG("ProxyHTMLStripComments", ap_set_flag_slot,
+ (void*)APR_OFFSETOF(proxy_html_conf, strip_comments),
+ RSRC_CONF|ACCESS_CONF, "Strip out comments"),
+ AP_INIT_TAKE1("ProxyHTMLBufSize", ap_set_int_slot,
+ (void*)APR_OFFSETOF(proxy_html_conf, bufsz),
+ RSRC_CONF|ACCESS_CONF, "Buffer size"),
+ AP_INIT_TAKE1("ProxyHTMLCharsetOut", ap_set_string_slot,
+ (void*)APR_OFFSETOF(proxy_html_conf, charset_out),
+ RSRC_CONF|ACCESS_CONF, "Usage: ProxyHTMLCharsetOut charset"),
+ AP_INIT_FLAG("ProxyHTMLEnable", ap_set_flag_slot,
+ (void*)APR_OFFSETOF(proxy_html_conf, enabled),
+ RSRC_CONF|ACCESS_CONF,
+ "Enable proxy-html and xml2enc filters"),
+ { NULL }
+};
+static int mod_proxy_html(apr_pool_t *p, apr_pool_t *p1, apr_pool_t *p2)
+{
+ seek_meta = ap_pregcomp(p, "<meta[^>]*(http-equiv)[^>]*>",
+ AP_REG_EXTENDED|AP_REG_ICASE);
+ seek_content = apr_strmatch_precompile(p, "content", 0);
+ memset(&sax, 0, sizeof(htmlSAXHandler));
+ sax.startElement = pstartElement;
+ sax.endElement = pendElement;
+ sax.characters = pcharacters;
+ sax.comment = pcomment;
+ sax.cdataBlock = pcdata;
+ xml2enc_charset = APR_RETRIEVE_OPTIONAL_FN(xml2enc_charset);
+ xml2enc_filter = APR_RETRIEVE_OPTIONAL_FN(xml2enc_filter);
+ if (!xml2enc_charset) {
+ ap_log_perror(APLOG_MARK, APLOG_NOTICE, 0, p2, APLOGNO(01425)
+ "I18n support in mod_proxy_html requires mod_xml2enc. "
+ "Without it, non-ASCII characters in proxied pages are "
+ "likely to display incorrectly.");
+ }
+
+ /* old_expr only needs to last the life of the config phase */
+ old_expr = ap_rxplus_compile(p1, "s/^(!)?(\\w+)((=)(.+))?$/reqenv('$2')$1$4'$5'/");
+ return OK;
+}
+static void proxy_html_insert(request_rec *r)
+{
+ proxy_html_conf *cfg;
+ cfg = ap_get_module_config(r->per_dir_config, &proxy_html_module);
+ if (cfg->enabled) {
+ if (xml2enc_filter)
+ xml2enc_filter(r, NULL, ENCIO_INPUT_CHECKS);
+ ap_add_output_filter("proxy-html", NULL, r, r->connection);
+ }
+}
+static void proxy_html_hooks(apr_pool_t *p)
+{
+ static const char *aszSucc[] = { "mod_filter.c", NULL };
+ ap_register_output_filter_protocol("proxy-html", proxy_html_filter,
+ NULL, AP_FTYPE_RESOURCE,
+ AP_FILTER_PROTO_CHANGE|AP_FILTER_PROTO_CHANGE_LENGTH);
+ /* move this to pre_config so old_expr is available to interpret
+ * old-style conditions on URL maps.
+ */
+ ap_hook_pre_config(mod_proxy_html, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_insert_filter(proxy_html_insert, NULL, aszSucc, APR_HOOK_MIDDLE);
+}
+
+AP_DECLARE_MODULE(proxy_html) = {
+ STANDARD20_MODULE_STUFF,
+ proxy_html_config,
+ proxy_html_merge,
+ NULL,
+ NULL,
+ proxy_html_cmds,
+ proxy_html_hooks
+};
diff --git a/modules/filters/mod_proxy_html.dep b/modules/filters/mod_proxy_html.dep
new file mode 100644
index 0000000..1a6ed08
--- /dev/null
+++ b/modules/filters/mod_proxy_html.dep
@@ -0,0 +1,58 @@
+# Microsoft Developer Studio Generated Dependency File, included by mod_proxy_html.mak
+
+..\..\build\win32\httpd.rc : \
+ "..\..\include\ap_release.h"\
+
+
+.\mod_proxy_html.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_strmatch.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\mod_xml2enc.h"\
+
diff --git a/modules/filters/mod_proxy_html.dsp b/modules/filters/mod_proxy_html.dsp
new file mode 100644
index 0000000..1d5d54e
--- /dev/null
+++ b/modules/filters/mod_proxy_html.dsp
@@ -0,0 +1,123 @@
+# Microsoft Developer Studio Project File - Name="mod_proxy_html" - 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_proxy_html - 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_proxy_html.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_proxy_html.mak" CFG="mod_proxy_html - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_proxy_html - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_proxy_html - 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_proxy_html - 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 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/libxml2/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_html_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" /i "../../include" /i "../../srclib/apr/include" /I "../../srclib/apr-util/include" /i "../../srclib/libxml2/include" /d BIN_NAME="mod_proxy_html.so" /d LONG_NAME="proxy_html_module for Apache"
+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_proxy_html.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_html.so
+# ADD LINK32 kernel32.lib libxml2.lib /nologo /subsystem:windows /dll /incremental:no /libpath:"../../srclib/libxml2/win32/bin.msvc" /debug /out:".\Release\mod_proxy_html.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_html.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_proxy_html.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "mod_proxy_html - 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/libxml2/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_html_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" /i "../../include" /i "../../srclib/apr/include" /I "../../srclib/apr-util/include" /i "../../srclib/libxml2/include" /d BIN_NAME="mod_proxy_html.so" /d LONG_NAME="proxy_html_module for Apache"
+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_proxy_html.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_html.so
+# ADD LINK32 kernel32.lib libxml2.lib /nologo /subsystem:windows /dll /incremental:no /libpath:"../../srclib/libxml2/win32/bin.msvc" /debug /out:".\Debug\mod_proxy_html.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_html.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_proxy_html.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_proxy_html - Win32 Release"
+# Name "mod_proxy_html - Win32 Debug"
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\mod_xml2enc.h
+# End Source File
+# End Group
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=.\mod_proxy_html.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/modules/filters/mod_proxy_html.mak b/modules/filters/mod_proxy_html.mak
new file mode 100644
index 0000000..c3579be
--- /dev/null
+++ b/modules/filters/mod_proxy_html.mak
@@ -0,0 +1,352 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on mod_proxy_html.dsp
+!IF "$(CFG)" == ""
+CFG=mod_proxy_html - Win32 Release
+!MESSAGE No configuration specified. Defaulting to mod_proxy_html - Win32 Release.
+!ENDIF
+
+!IF "$(CFG)" != "mod_proxy_html - Win32 Release" && "$(CFG)" != "mod_proxy_html - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!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_proxy_html.mak" CFG="mod_proxy_html - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_proxy_html - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_proxy_html - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(CFG)" == "mod_proxy_html - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_proxy_html.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_proxy_html.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\httpd.res"
+ -@erase "$(INTDIR)\mod_proxy_html.obj"
+ -@erase "$(INTDIR)\mod_proxy_html_src.idb"
+ -@erase "$(INTDIR)\mod_proxy_html_src.pdb"
+ -@erase "$(OUTDIR)\mod_proxy_html.exp"
+ -@erase "$(OUTDIR)\mod_proxy_html.lib"
+ -@erase "$(OUTDIR)\mod_proxy_html.pdb"
+ -@erase "$(OUTDIR)\mod_proxy_html.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/libxml2/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_html_src" /FD /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\httpd.res" /i "../../include" /i "../../srclib/apr/include" /i "../../srclib/apr-util/include" /i "../../srclib/libxml2/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_html.so" /d LONG_NAME="proxy_html_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_html.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib libxml2.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_html.pdb" /debug /out:"$(OUTDIR)\mod_proxy_html.so" /implib:"$(OUTDIR)\mod_proxy_html.lib" /libpath:"../../srclib/libxml2/win32/bin.msvc" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_html.so /opt:ref
+LINK32_OBJS= \
+ "$(INTDIR)\mod_proxy_html.obj" \
+ "$(INTDIR)\httpd.res" \
+ "..\..\srclib\apr\Release\libapr-1.lib" \
+ "..\..\srclib\apr-util\Release\libaprutil-1.lib" \
+ "..\..\Release\libhttpd.lib"
+
+"$(OUTDIR)\mod_proxy_html.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Release\mod_proxy_html.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_html.so"
+ if exist .\Release\mod_proxy_html.so.manifest mt.exe -manifest .\Release\mod_proxy_html.so.manifest -outputresource:.\Release\mod_proxy_html.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ELSEIF "$(CFG)" == "mod_proxy_html - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_proxy_html.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_proxy_html.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\httpd.res"
+ -@erase "$(INTDIR)\mod_proxy_html.obj"
+ -@erase "$(INTDIR)\mod_proxy_html_src.idb"
+ -@erase "$(INTDIR)\mod_proxy_html_src.pdb"
+ -@erase "$(OUTDIR)\mod_proxy_html.exp"
+ -@erase "$(OUTDIR)\mod_proxy_html.lib"
+ -@erase "$(OUTDIR)\mod_proxy_html.pdb"
+ -@erase "$(OUTDIR)\mod_proxy_html.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/libxml2/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_html_src" /FD /EHsc /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\httpd.res" /i "../../include" /i "../../srclib/apr/include" /i "../../srclib/apr-util/include" /i "../../srclib/libxml2/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_html.so" /d LONG_NAME="proxy_html_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_html.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib libxml2.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_html.pdb" /debug /out:"$(OUTDIR)\mod_proxy_html.so" /implib:"$(OUTDIR)\mod_proxy_html.lib" /libpath:"../../srclib/libxml2/win32/bin.msvc" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_html.so
+LINK32_OBJS= \
+ "$(INTDIR)\mod_proxy_html.obj" \
+ "$(INTDIR)\httpd.res" \
+ "..\..\srclib\apr\Debug\libapr-1.lib" \
+ "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \
+ "..\..\Debug\libhttpd.lib"
+
+"$(OUTDIR)\mod_proxy_html.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Debug\mod_proxy_html.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_html.so"
+ if exist .\Debug\mod_proxy_html.so.manifest mt.exe -manifest .\Debug\mod_proxy_html.so.manifest -outputresource:.\Debug\mod_proxy_html.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+
+!IF "$(NO_EXTERNAL_DEPS)" != "1"
+!IF EXISTS("mod_proxy_html.dep")
+!INCLUDE "mod_proxy_html.dep"
+!ELSE
+!MESSAGE Warning: cannot find "mod_proxy_html.dep"
+!ENDIF
+!ENDIF
+
+
+!IF "$(CFG)" == "mod_proxy_html - Win32 Release" || "$(CFG)" == "mod_proxy_html - Win32 Debug"
+SOURCE=..\..\build\win32\httpd.rc
+
+!IF "$(CFG)" == "mod_proxy_html - Win32 Release"
+
+
+"$(INTDIR)\httpd.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\httpd.res" /i "../../include" /i "../../srclib/apr/include" /i "../../srclib/apr-util/include" /i "../../srclib/libxml2/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy_html.so" /d LONG_NAME="proxy_html_module for Apache" $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "mod_proxy_html - Win32 Debug"
+
+
+"$(INTDIR)\httpd.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\httpd.res" /i "../../include" /i "../../srclib/apr/include" /i "../../srclib/apr-util/include" /i "../../srclib/libxml2/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_html.so" /d LONG_NAME="proxy_html_module for Apache" $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\mod_proxy_html.c
+
+"$(INTDIR)\mod_proxy_html.obj" : $(SOURCE) "$(INTDIR)"
+
+
+!IF "$(CFG)" == "mod_proxy_html - Win32 Release"
+
+"libapr - Win32 Release" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_proxy_html - Win32 Debug"
+
+"libapr - Win32 Debug" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_proxy_html - Win32 Release"
+
+"libaprutil - Win32 Release" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_proxy_html - Win32 Debug"
+
+"libaprutil - Win32 Debug" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_proxy_html - Win32 Release"
+
+"libhttpd - Win32 Release" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 ReleaseCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_proxy_html - Win32 Debug"
+
+"libhttpd - Win32 Debug" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 DebugCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ENDIF
+
+
+!ENDIF
+
diff --git a/modules/filters/mod_ratelimit.c b/modules/filters/mod_ratelimit.c
new file mode 100644
index 0000000..d16eb39
--- /dev/null
+++ b/modules/filters/mod_ratelimit.c
@@ -0,0 +1,340 @@
+/* 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.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_log.h"
+#include "util_filter.h"
+
+#include "mod_ratelimit.h"
+
+#define RATE_LIMIT_FILTER_NAME "RATE_LIMIT"
+#define RATE_INTERVAL_MS (200)
+
+typedef enum rl_state_e
+{
+ RATE_LIMIT,
+ RATE_FULLSPEED
+} rl_state_e;
+
+typedef struct rl_ctx_t
+{
+ int speed;
+ int chunk_size;
+ int burst;
+ int do_sleep;
+ rl_state_e state;
+ apr_bucket_brigade *tmpbb;
+ apr_bucket_brigade *holdingbb;
+} rl_ctx_t;
+
+#if defined(RLFDEBUG)
+static void brigade_dump(request_rec *r, apr_bucket_brigade *bb)
+{
+ apr_bucket *e;
+ int i = 0;
+
+ for (e = APR_BRIGADE_FIRST(bb);
+ e != APR_BRIGADE_SENTINEL(bb); e = APR_BUCKET_NEXT(e), i++) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(03193)
+ "brigade: [%d] %s", i, e->type->name);
+
+ }
+}
+#endif /* RLFDEBUG */
+
+static apr_status_t
+rate_limit_filter(ap_filter_t *f, apr_bucket_brigade *bb)
+{
+ apr_status_t rv = APR_SUCCESS;
+ rl_ctx_t *ctx = f->ctx;
+ apr_bucket_alloc_t *ba = f->r->connection->bucket_alloc;
+
+ /* Set up our rl_ctx_t on first use */
+ if (ctx == NULL) {
+ const char *rl = NULL;
+ int ratelimit;
+ int burst = 0;
+
+ /* no subrequests. */
+ if (f->r->main != NULL) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ /* Configuration: rate limit */
+ rl = apr_table_get(f->r->subprocess_env, "rate-limit");
+
+ if (rl == NULL) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ /* rl is in kilo bytes / second */
+ ratelimit = atoi(rl) * 1024;
+ if (ratelimit <= 0) {
+ /* remove ourselves */
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r,
+ APLOGNO(03488) "rl: disabling: rate-limit = %s (too high?)", rl);
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ /* Configuration: optional initial burst */
+ rl = apr_table_get(f->r->subprocess_env, "rate-initial-burst");
+ if (rl != NULL) {
+ burst = atoi(rl) * 1024;
+ if (burst <= 0) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r,
+ APLOGNO(03489) "rl: disabling burst: rate-initial-burst = %s (too high?)", rl);
+ burst = 0;
+ }
+ }
+
+ /* Set up our context */
+ ctx = apr_palloc(f->r->pool, sizeof(rl_ctx_t));
+ f->ctx = ctx;
+ ctx->state = RATE_LIMIT;
+ ctx->speed = ratelimit;
+ ctx->burst = burst;
+ ctx->do_sleep = 0;
+
+ /* calculate how many bytes / interval we want to send */
+ /* speed is bytes / second, so, how many (speed / 1000 % interval) */
+ ctx->chunk_size = (ctx->speed / (1000 / RATE_INTERVAL_MS));
+ ctx->tmpbb = apr_brigade_create(f->r->pool, ba);
+ ctx->holdingbb = apr_brigade_create(f->r->pool, ba);
+ }
+ else {
+ APR_BRIGADE_PREPEND(bb, ctx->holdingbb);
+ }
+
+ while (!APR_BRIGADE_EMPTY(bb)) {
+ apr_bucket *e;
+
+ if (ctx->state == RATE_FULLSPEED) {
+ /* Find where we 'stop' going full speed. */
+ for (e = APR_BRIGADE_FIRST(bb);
+ e != APR_BRIGADE_SENTINEL(bb); e = APR_BUCKET_NEXT(e)) {
+ if (AP_RL_BUCKET_IS_END(e)) {
+ apr_brigade_split_ex(bb, e, ctx->holdingbb);
+ ctx->state = RATE_LIMIT;
+ break;
+ }
+ }
+
+ e = apr_bucket_flush_create(ba);
+ APR_BRIGADE_INSERT_TAIL(bb, e);
+ rv = ap_pass_brigade(f->next, bb);
+ apr_brigade_cleanup(bb);
+
+ if (rv != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, f->r, APLOGNO(01455)
+ "rl: full speed brigade pass failed.");
+ return rv;
+ }
+ }
+ else {
+ for (e = APR_BRIGADE_FIRST(bb);
+ e != APR_BRIGADE_SENTINEL(bb); e = APR_BUCKET_NEXT(e)) {
+ if (AP_RL_BUCKET_IS_START(e)) {
+ apr_brigade_split_ex(bb, e, ctx->holdingbb);
+ ctx->state = RATE_FULLSPEED;
+ break;
+ }
+ }
+
+ while (!APR_BRIGADE_EMPTY(bb)) {
+ apr_off_t len = ctx->chunk_size + ctx->burst;
+
+ APR_BRIGADE_CONCAT(ctx->tmpbb, bb);
+
+ /*
+ * Pull next chunk of data; the initial amount is our
+ * burst allotment (if any) plus a chunk. All subsequent
+ * iterations are just chunks with whatever remaining
+ * burst amounts we have left (in case not done in the
+ * first bucket).
+ */
+ rv = apr_brigade_partition(ctx->tmpbb, len, &e);
+ if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, APLOGNO(01456)
+ "rl: partition failed.");
+ return rv;
+ }
+ /* Send next metadata now if any */
+ while (e != APR_BRIGADE_SENTINEL(ctx->tmpbb)
+ && APR_BUCKET_IS_METADATA(e)) {
+ e = APR_BUCKET_NEXT(e);
+ }
+ if (e != APR_BRIGADE_SENTINEL(ctx->tmpbb)) {
+ apr_brigade_split_ex(ctx->tmpbb, e, bb);
+ }
+ else {
+ apr_brigade_length(ctx->tmpbb, 1, &len);
+ }
+
+ /*
+ * Adjust the burst amount depending on how much
+ * we've done up to now.
+ */
+ if (ctx->burst) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r,
+ APLOGNO(03485) "rl: burst %d; len %"APR_OFF_T_FMT, ctx->burst, len);
+ if (len < ctx->burst) {
+ ctx->burst -= len;
+ }
+ else {
+ ctx->burst = 0;
+ }
+ }
+
+ e = APR_BRIGADE_LAST(ctx->tmpbb);
+ if (APR_BUCKET_IS_EOS(e)) {
+ ap_remove_output_filter(f);
+ }
+ else if (!APR_BUCKET_IS_FLUSH(e)) {
+ if (APR_BRIGADE_EMPTY(bb)) {
+ /* Wait for more (or next call) */
+ break;
+ }
+ e = apr_bucket_flush_create(ba);
+ APR_BRIGADE_INSERT_TAIL(ctx->tmpbb, e);
+ }
+
+#if defined(RLFDEBUG)
+ brigade_dump(f->r, ctx->tmpbb);
+ brigade_dump(f->r, bb);
+#endif /* RLFDEBUG */
+
+ if (ctx->do_sleep) {
+ apr_sleep(RATE_INTERVAL_MS * 1000);
+ }
+ else {
+ ctx->do_sleep = 1;
+ }
+
+ rv = ap_pass_brigade(f->next, ctx->tmpbb);
+ apr_brigade_cleanup(ctx->tmpbb);
+
+ if (rv != APR_SUCCESS) {
+ /* Most often, user disconnects from stream */
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, f->r, APLOGNO(01457)
+ "rl: brigade pass failed.");
+ return rv;
+ }
+ }
+ }
+
+ if (!APR_BRIGADE_EMPTY(ctx->holdingbb)) {
+ /* Any rate-limited data in tmpbb is sent unlimited along
+ * with the rest.
+ */
+ APR_BRIGADE_CONCAT(bb, ctx->tmpbb);
+ APR_BRIGADE_CONCAT(bb, ctx->holdingbb);
+ }
+ }
+
+#if defined(RLFDEBUG)
+ brigade_dump(f->r, ctx->tmpbb);
+#endif /* RLFDEBUG */
+
+ /* Save remaining tmpbb with the correct lifetime for the next call */
+ return ap_save_brigade(f, &ctx->holdingbb, &ctx->tmpbb, f->r->pool);
+}
+
+
+static apr_status_t
+rl_bucket_read(apr_bucket *b, const char **str,
+ apr_size_t *len, apr_read_type_e block)
+{
+ *str = NULL;
+ *len = 0;
+ return APR_SUCCESS;
+}
+
+AP_RL_DECLARE(apr_bucket *)
+ ap_rl_end_create(apr_bucket_alloc_t *list)
+{
+ apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+
+ APR_BUCKET_INIT(b);
+ b->free = apr_bucket_free;
+ b->list = list;
+ b->length = 0;
+ b->start = 0;
+ b->data = NULL;
+ b->type = &ap_rl_bucket_type_end;
+
+ return b;
+}
+
+AP_RL_DECLARE(apr_bucket *)
+ ap_rl_start_create(apr_bucket_alloc_t *list)
+{
+ apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+
+ APR_BUCKET_INIT(b);
+ b->free = apr_bucket_free;
+ b->list = list;
+ b->length = 0;
+ b->start = 0;
+ b->data = NULL;
+ b->type = &ap_rl_bucket_type_start;
+
+ return b;
+}
+
+
+
+AP_RL_DECLARE_DATA const apr_bucket_type_t ap_rl_bucket_type_end = {
+ "RL_END", 5, APR_BUCKET_METADATA,
+ apr_bucket_destroy_noop,
+ rl_bucket_read,
+ apr_bucket_setaside_noop,
+ apr_bucket_split_notimpl,
+ apr_bucket_simple_copy
+};
+
+
+AP_RL_DECLARE_DATA const apr_bucket_type_t ap_rl_bucket_type_start = {
+ "RL_START", 5, APR_BUCKET_METADATA,
+ apr_bucket_destroy_noop,
+ rl_bucket_read,
+ apr_bucket_setaside_noop,
+ apr_bucket_split_notimpl,
+ apr_bucket_simple_copy
+};
+
+
+
+
+static void register_hooks(apr_pool_t *p)
+{
+ /* run after mod_deflate etc etc, but not at connection level, ie, mod_ssl. */
+ ap_register_output_filter(RATE_LIMIT_FILTER_NAME, rate_limit_filter,
+ NULL, AP_FTYPE_CONNECTION - 1);
+}
+
+AP_DECLARE_MODULE(ratelimit) = {
+ STANDARD20_MODULE_STUFF,
+ NULL, /* create per-directory config structure */
+ NULL, /* merge per-directory config structures */
+ NULL, /* create per-server config structure */
+ NULL, /* merge per-server config structures */
+ NULL, /* command apr_table_t */
+ register_hooks
+};
diff --git a/modules/filters/mod_ratelimit.dep b/modules/filters/mod_ratelimit.dep
new file mode 100644
index 0000000..5015e98
--- /dev/null
+++ b/modules/filters/mod_ratelimit.dep
@@ -0,0 +1,45 @@
+# Microsoft Developer Studio Generated Dependency File, included by mod_ratelimit.mak
+
+..\..\build\win32\httpd.rc : \
+ "..\..\include\ap_release.h"\
+
+
+.\mod_ratelimit.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\mod_ratelimit.h"\
+
diff --git a/modules/filters/mod_ratelimit.dsp b/modules/filters/mod_ratelimit.dsp
new file mode 100644
index 0000000..99bb595
--- /dev/null
+++ b/modules/filters/mod_ratelimit.dsp
@@ -0,0 +1,115 @@
+# Microsoft Developer Studio Project File - Name="mod_ratelimit" - 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_ratelimit - 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_ratelimit.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_ratelimit.mak" CFG="mod_ratelimit - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_ratelimit - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_ratelimit - 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_ratelimit - 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 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "AP_RL_DECLARE_EXPORT" /Fd"Release\mod_ratelimit_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 /fo"Release/mod_ratelimit.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_ratelimit.so" /d LONG_NAME="ratelimit_module for Apache"
+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_ratelimit.so" /base:@..\..\os\win32\BaseAddr.ref,mod_ratelimit.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_ratelimit.so" /base:@..\..\os\win32\BaseAddr.ref,mod_ratelimit.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_ratelimit.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "mod_ratelimit - 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" /D "AP_RL_DECLARE_EXPORT" /Fd"Debug\mod_ratelimit_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 /fo"Debug/mod_ratelimit.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_ratelimit.so" /d LONG_NAME="ratelimit_module for Apache"
+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_ratelimit.so" /base:@..\..\os\win32\BaseAddr.ref,mod_ratelimit.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_ratelimit.so" /base:@..\..\os\win32\BaseAddr.ref,mod_ratelimit.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_ratelimit.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_ratelimit - Win32 Release"
+# Name "mod_ratelimit - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\mod_ratelimit.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mod_ratelimit.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
diff --git a/modules/filters/mod_ratelimit.h b/modules/filters/mod_ratelimit.h
new file mode 100644
index 0000000..6c69bb1
--- /dev/null
+++ b/modules/filters/mod_ratelimit.h
@@ -0,0 +1,51 @@
+/* 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_RATELIMIT_H_
+#define _MOD_RATELIMIT_H_
+
+/* Create a set of AP_RL_DECLARE(type), AP_RL_DECLARE_NONSTD(type) and
+ * AP_RL_DECLARE_DATA with appropriate export and import tags for the platform
+ */
+#if !defined(WIN32)
+#define AP_RL_DECLARE(type) type
+#define AP_RL_DECLARE_NONSTD(type) type
+#define AP_RL_DECLARE_DATA
+#elif defined(AP_RL_DECLARE_STATIC)
+#define AP_RL_DECLARE(type) type __stdcall
+#define AP_RL_DECLARE_NONSTD(type) type
+#define AP_RL_DECLARE_DATA
+#elif defined(AP_RL_DECLARE_EXPORT)
+#define AP_RL_DECLARE(type) __declspec(dllexport) type __stdcall
+#define AP_RL_DECLARE_NONSTD(type) __declspec(dllexport) type
+#define AP_RL_DECLARE_DATA __declspec(dllexport)
+#else
+#define AP_RL_DECLARE(type) __declspec(dllimport) type __stdcall
+#define AP_RL_DECLARE_NONSTD(type) __declspec(dllimport) type
+#define AP_RL_DECLARE_DATA __declspec(dllimport)
+#endif
+
+AP_RL_DECLARE_DATA extern const apr_bucket_type_t ap_rl_bucket_type_end;
+AP_RL_DECLARE_DATA extern const apr_bucket_type_t ap_rl_bucket_type_start;
+
+#define AP_RL_BUCKET_IS_END(e) (e->type == &ap_rl_bucket_type_end)
+#define AP_RL_BUCKET_IS_START(e) (e->type == &ap_rl_bucket_type_start)
+
+/* TODO: Make these Optional Functions, so that module load order doesn't matter. */
+AP_RL_DECLARE(apr_bucket*) ap_rl_end_create(apr_bucket_alloc_t *list);
+AP_RL_DECLARE(apr_bucket*) ap_rl_start_create(apr_bucket_alloc_t *list);
+
+#endif
diff --git a/modules/filters/mod_ratelimit.mak b/modules/filters/mod_ratelimit.mak
new file mode 100644
index 0000000..e50e892
--- /dev/null
+++ b/modules/filters/mod_ratelimit.mak
@@ -0,0 +1,353 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on mod_ratelimit.dsp
+!IF "$(CFG)" == ""
+CFG=mod_ratelimit - Win32 Release
+!MESSAGE No configuration specified. Defaulting to mod_ratelimit - Win32 Release.
+!ENDIF
+
+!IF "$(CFG)" != "mod_ratelimit - Win32 Release" && "$(CFG)" != "mod_ratelimit - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!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_ratelimit.mak" CFG="mod_ratelimit - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_ratelimit - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_ratelimit - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(CFG)" == "mod_ratelimit - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_ratelimit.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_ratelimit.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_ratelimit.obj"
+ -@erase "$(INTDIR)\mod_ratelimit.res"
+ -@erase "$(INTDIR)\mod_ratelimit_src.idb"
+ -@erase "$(INTDIR)\mod_ratelimit_src.pdb"
+ -@erase "$(OUTDIR)\mod_ratelimit.exp"
+ -@erase "$(OUTDIR)\mod_ratelimit.lib"
+ -@erase "$(OUTDIR)\mod_ratelimit.pdb"
+ -@erase "$(OUTDIR)\mod_ratelimit.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "AP_RL_DECLARE_EXPORT" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_ratelimit_src" /FD /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_ratelimit.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_ratelimit.so" /d LONG_NAME="ratelimit_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_ratelimit.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_ratelimit.pdb" /debug /out:"$(OUTDIR)\mod_ratelimit.so" /implib:"$(OUTDIR)\mod_ratelimit.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_ratelimit.so /opt:ref
+LINK32_OBJS= \
+ "$(INTDIR)\mod_ratelimit.obj" \
+ "$(INTDIR)\mod_ratelimit.res" \
+ "..\..\srclib\apr\Release\libapr-1.lib" \
+ "..\..\srclib\apr-util\Release\libaprutil-1.lib" \
+ "..\..\Release\libhttpd.lib"
+
+"$(OUTDIR)\mod_ratelimit.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Release\mod_ratelimit.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_ratelimit.so"
+ if exist .\Release\mod_ratelimit.so.manifest mt.exe -manifest .\Release\mod_ratelimit.so.manifest -outputresource:.\Release\mod_ratelimit.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ELSEIF "$(CFG)" == "mod_ratelimit - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_ratelimit.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_ratelimit.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_ratelimit.obj"
+ -@erase "$(INTDIR)\mod_ratelimit.res"
+ -@erase "$(INTDIR)\mod_ratelimit_src.idb"
+ -@erase "$(INTDIR)\mod_ratelimit_src.pdb"
+ -@erase "$(OUTDIR)\mod_ratelimit.exp"
+ -@erase "$(OUTDIR)\mod_ratelimit.lib"
+ -@erase "$(OUTDIR)\mod_ratelimit.pdb"
+ -@erase "$(OUTDIR)\mod_ratelimit.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "AP_RL_DECLARE_EXPORT" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_ratelimit_src" /FD /EHsc /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_ratelimit.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_ratelimit.so" /d LONG_NAME="ratelimit_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_ratelimit.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_ratelimit.pdb" /debug /out:"$(OUTDIR)\mod_ratelimit.so" /implib:"$(OUTDIR)\mod_ratelimit.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_ratelimit.so
+LINK32_OBJS= \
+ "$(INTDIR)\mod_ratelimit.obj" \
+ "$(INTDIR)\mod_ratelimit.res" \
+ "..\..\srclib\apr\Debug\libapr-1.lib" \
+ "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \
+ "..\..\Debug\libhttpd.lib"
+
+"$(OUTDIR)\mod_ratelimit.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Debug\mod_ratelimit.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_ratelimit.so"
+ if exist .\Debug\mod_ratelimit.so.manifest mt.exe -manifest .\Debug\mod_ratelimit.so.manifest -outputresource:.\Debug\mod_ratelimit.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+
+!IF "$(NO_EXTERNAL_DEPS)" != "1"
+!IF EXISTS("mod_ratelimit.dep")
+!INCLUDE "mod_ratelimit.dep"
+!ELSE
+!MESSAGE Warning: cannot find "mod_ratelimit.dep"
+!ENDIF
+!ENDIF
+
+
+!IF "$(CFG)" == "mod_ratelimit - Win32 Release" || "$(CFG)" == "mod_ratelimit - Win32 Debug"
+
+!IF "$(CFG)" == "mod_ratelimit - Win32 Release"
+
+"libapr - Win32 Release" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_ratelimit - Win32 Debug"
+
+"libapr - Win32 Debug" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_ratelimit - Win32 Release"
+
+"libaprutil - Win32 Release" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_ratelimit - Win32 Debug"
+
+"libaprutil - Win32 Debug" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_ratelimit - Win32 Release"
+
+"libhttpd - Win32 Release" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 ReleaseCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_ratelimit - Win32 Debug"
+
+"libhttpd - Win32 Debug" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 DebugCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ENDIF
+
+SOURCE=..\..\build\win32\httpd.rc
+
+!IF "$(CFG)" == "mod_ratelimit - Win32 Release"
+
+
+"$(INTDIR)\mod_ratelimit.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_ratelimit.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_ratelimit.so" /d LONG_NAME="ratelimit_module for Apache" $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "mod_ratelimit - Win32 Debug"
+
+
+"$(INTDIR)\mod_ratelimit.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_ratelimit.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_ratelimit.so" /d LONG_NAME="ratelimit_module for Apache" $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\mod_ratelimit.c
+
+"$(INTDIR)\mod_ratelimit.obj" : $(SOURCE) "$(INTDIR)"
+
+
+
+!ENDIF
+
diff --git a/modules/filters/mod_reflector.c b/modules/filters/mod_reflector.c
new file mode 100644
index 0000000..5979cb8
--- /dev/null
+++ b/modules/filters/mod_reflector.c
@@ -0,0 +1,231 @@
+/* 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.
+ */
+#include "apr_strings.h"
+#include "apr_tables.h"
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "mod_core.h"
+
+module AP_MODULE_DECLARE_DATA reflector_module;
+
+typedef struct {
+ apr_table_t *headers;
+} reflector_cfg;
+
+static int header_do(void *dummy, const char *key, const char *value)
+{
+ request_rec *r = (request_rec *) dummy;
+ const char *payload;
+
+ payload = apr_table_get(r->headers_in, key);
+ if (payload) {
+ apr_table_setn(r->headers_out, value, payload);
+ }
+
+ return 1;
+}
+
+static int reflector_handler(request_rec * r)
+{
+ apr_bucket_brigade *bbin, *bbout;
+ reflector_cfg *conf;
+ apr_status_t status;
+
+ if (strcmp(r->handler, "reflector")) {
+ return DECLINED;
+ }
+
+ conf = (reflector_cfg *) ap_get_module_config(r->per_dir_config,
+ &reflector_module);
+
+ ap_allow_methods(r, 1, "POST", "OPTIONS", NULL);
+
+ if (r->method_number == M_OPTIONS) {
+ return ap_send_http_options(r);
+ }
+
+ else if (r->method_number == M_POST) {
+ const char *content_length, *content_type;
+ int seen_eos;
+
+ /*
+ * Sometimes we'll get in a state where the input handling has
+ * detected an error where we want to drop the connection, so if
+ * that's the case, don't read the data as that is what we're trying
+ * to avoid.
+ *
+ * This function is also a no-op on a subrequest.
+ */
+ if (r->main || r->connection->keepalive == AP_CONN_CLOSE ||
+ ap_status_drops_connection(r->status)) {
+ return OK;
+ }
+
+ /* copy headers from in to out if configured */
+ apr_table_do(header_do, r, conf->headers, NULL);
+
+ /* last modified defaults to now, unless otherwise set on the way in */
+ if (!apr_table_get(r->headers_out, "Last-Modified")) {
+ ap_update_mtime(r, apr_time_now());
+ ap_set_last_modified(r);
+ }
+ ap_set_accept_ranges(r);
+
+ /* reflect the content length, if present */
+ if ((content_length = apr_table_get(r->headers_in, "Content-Length"))) {
+ apr_off_t clen;
+
+ if (!ap_parse_strict_length(&clen, content_length)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10243)
+ "reflector_handler: invalid content-length '%s'",
+ content_length);
+ return HTTP_BAD_REQUEST;
+ }
+
+ ap_set_content_length(r, clen);
+ }
+
+ /* reflect the content type, if present */
+ if ((content_type = apr_table_get(r->headers_in, "Content-Type"))) {
+
+ ap_set_content_type(r, content_type);
+
+ }
+
+ bbin = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+ bbout = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+
+ seen_eos = 0;
+ do {
+ apr_bucket *bucket;
+
+ status = ap_get_brigade(r->input_filters, bbin, AP_MODE_READBYTES,
+ APR_BLOCK_READ, HUGE_STRING_LEN);
+
+ if (status != APR_SUCCESS) {
+ apr_brigade_destroy(bbin);
+ return ap_map_http_request_error(status, HTTP_BAD_REQUEST);
+ }
+
+ for (bucket = APR_BRIGADE_FIRST(bbin);
+ bucket != APR_BRIGADE_SENTINEL(bbin);
+ bucket = APR_BUCKET_NEXT(bucket)) {
+ const char *data;
+ apr_size_t len;
+
+ if (APR_BUCKET_IS_EOS(bucket)) {
+ seen_eos = 1;
+ break;
+ }
+
+ /* These are metadata buckets. */
+ if (bucket->length == 0) {
+ continue;
+ }
+
+ /*
+ * We MUST read because in case we have an unknown-length
+ * bucket or one that morphs, we want to exhaust it.
+ */
+ status = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
+ if (status != APR_SUCCESS) {
+ apr_brigade_destroy(bbin);
+ return HTTP_BAD_REQUEST;
+ }
+
+ apr_brigade_write(bbout, NULL, NULL, data, len);
+
+ status = ap_pass_brigade(r->output_filters, bbout);
+ if (status != APR_SUCCESS) {
+ /* no way to know what type of error occurred */
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(01410)
+ "reflector_handler: ap_pass_brigade returned %i",
+ status);
+ return AP_FILTER_ERROR;
+ }
+
+ }
+
+ apr_brigade_cleanup(bbin);
+
+ } while (!seen_eos);
+
+ return OK;
+
+ }
+
+ else {
+ return HTTP_METHOD_NOT_ALLOWED;
+ }
+
+}
+
+static void *create_reflector_dir_config(apr_pool_t * p, char *d)
+{
+ reflector_cfg *conf = apr_pcalloc(p, sizeof(reflector_cfg));
+
+ conf->headers = apr_table_make(p, 8);
+
+ return conf;
+}
+
+static void *merge_reflector_dir_config(apr_pool_t * p, void *basev, void *addv)
+{
+ reflector_cfg *new = (reflector_cfg *) apr_pcalloc(p,
+ sizeof(reflector_cfg));
+ reflector_cfg *add = (reflector_cfg *) addv;
+ reflector_cfg *base = (reflector_cfg *) basev;
+
+ new->headers = apr_table_overlay(p, add->headers, base->headers);
+
+ return new;
+}
+
+static const char *reflector_header(cmd_parms * cmd, void *dummy, const char *in,
+ const char *out)
+{
+ reflector_cfg *cfg = (reflector_cfg *) dummy;
+
+ apr_table_addn(cfg->headers, in, out ? out : in);
+
+ return NULL;
+}
+
+static void reflector_hooks(apr_pool_t * p)
+{
+ ap_hook_handler(reflector_handler, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+static const command_rec reflector_cmds[] = {
+ AP_INIT_TAKE12("ReflectorHeader", reflector_header, NULL, OR_OPTIONS,
+ "Header to reflect back in the response, with an optional new name."),
+ {NULL}
+};
+
+AP_DECLARE_MODULE(reflector) = {
+ STANDARD20_MODULE_STUFF,
+ create_reflector_dir_config,
+ merge_reflector_dir_config,
+ NULL,
+ NULL,
+ reflector_cmds,
+ reflector_hooks
+};
diff --git a/modules/filters/mod_reflector.dep b/modules/filters/mod_reflector.dep
new file mode 100644
index 0000000..dc91ddf
--- /dev/null
+++ b/modules/filters/mod_reflector.dep
@@ -0,0 +1,57 @@
+# Microsoft Developer Studio Generated Dependency File, included by mod_reflector.mak
+
+..\..\build\win32\httpd.rc : \
+ "..\..\include\ap_release.h"\
+
+
+.\mod_reflector.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\mod_core.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+
diff --git a/modules/filters/mod_reflector.dsp b/modules/filters/mod_reflector.dsp
new file mode 100644
index 0000000..6f284df
--- /dev/null
+++ b/modules/filters/mod_reflector.dsp
@@ -0,0 +1,111 @@
+# Microsoft Developer Studio Project File - Name="mod_reflector" - 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_reflector - 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_reflector.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_reflector.mak" CFG="mod_reflector - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_reflector - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_reflector - 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_reflector - 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 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "AP_RL_DECLARE_EXPORT" /Fd"Release\mod_reflector_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 /fo"Release/mod_reflector.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_reflector.so" /d LONG_NAME="reflector_module for Apache"
+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_reflector.so" /base:@..\..\os\win32\BaseAddr.ref,mod_reflector.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_reflector.so" /base:@..\..\os\win32\BaseAddr.ref,mod_reflector.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_reflector.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "mod_reflector - 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" /D "AP_RL_DECLARE_EXPORT" /Fd"Debug\mod_reflector_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 /fo"Debug/mod_reflector.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_reflector.so" /d LONG_NAME="reflector_module for Apache"
+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_reflector.so" /base:@..\..\os\win32\BaseAddr.ref,mod_reflector.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_reflector.so" /base:@..\..\os\win32\BaseAddr.ref,mod_reflector.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_reflector.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_reflector - Win32 Release"
+# Name "mod_reflector - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\mod_reflector.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
diff --git a/modules/filters/mod_reflector.mak b/modules/filters/mod_reflector.mak
new file mode 100644
index 0000000..d38b35c
--- /dev/null
+++ b/modules/filters/mod_reflector.mak
@@ -0,0 +1,353 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on mod_reflector.dsp
+!IF "$(CFG)" == ""
+CFG=mod_reflector - Win32 Release
+!MESSAGE No configuration specified. Defaulting to mod_reflector - Win32 Release.
+!ENDIF
+
+!IF "$(CFG)" != "mod_reflector - Win32 Release" && "$(CFG)" != "mod_reflector - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!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_reflector.mak" CFG="mod_reflector - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_reflector - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_reflector - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(CFG)" == "mod_reflector - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_reflector.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_reflector.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_reflector.obj"
+ -@erase "$(INTDIR)\mod_reflector.res"
+ -@erase "$(INTDIR)\mod_reflector_src.idb"
+ -@erase "$(INTDIR)\mod_reflector_src.pdb"
+ -@erase "$(OUTDIR)\mod_reflector.exp"
+ -@erase "$(OUTDIR)\mod_reflector.lib"
+ -@erase "$(OUTDIR)\mod_reflector.pdb"
+ -@erase "$(OUTDIR)\mod_reflector.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "AP_RL_DECLARE_EXPORT" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_reflector_src" /FD /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_reflector.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_reflector.so" /d LONG_NAME="reflector_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_reflector.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_reflector.pdb" /debug /out:"$(OUTDIR)\mod_reflector.so" /implib:"$(OUTDIR)\mod_reflector.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_reflector.so /opt:ref
+LINK32_OBJS= \
+ "$(INTDIR)\mod_reflector.obj" \
+ "$(INTDIR)\mod_reflector.res" \
+ "..\..\srclib\apr\Release\libapr-1.lib" \
+ "..\..\srclib\apr-util\Release\libaprutil-1.lib" \
+ "..\..\Release\libhttpd.lib"
+
+"$(OUTDIR)\mod_reflector.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Release\mod_reflector.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_reflector.so"
+ if exist .\Release\mod_reflector.so.manifest mt.exe -manifest .\Release\mod_reflector.so.manifest -outputresource:.\Release\mod_reflector.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ELSEIF "$(CFG)" == "mod_reflector - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_reflector.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_reflector.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_reflector.obj"
+ -@erase "$(INTDIR)\mod_reflector.res"
+ -@erase "$(INTDIR)\mod_reflector_src.idb"
+ -@erase "$(INTDIR)\mod_reflector_src.pdb"
+ -@erase "$(OUTDIR)\mod_reflector.exp"
+ -@erase "$(OUTDIR)\mod_reflector.lib"
+ -@erase "$(OUTDIR)\mod_reflector.pdb"
+ -@erase "$(OUTDIR)\mod_reflector.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "AP_RL_DECLARE_EXPORT" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_reflector_src" /FD /EHsc /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_reflector.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_reflector.so" /d LONG_NAME="reflector_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_reflector.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_reflector.pdb" /debug /out:"$(OUTDIR)\mod_reflector.so" /implib:"$(OUTDIR)\mod_reflector.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_reflector.so
+LINK32_OBJS= \
+ "$(INTDIR)\mod_reflector.obj" \
+ "$(INTDIR)\mod_reflector.res" \
+ "..\..\srclib\apr\Debug\libapr-1.lib" \
+ "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \
+ "..\..\Debug\libhttpd.lib"
+
+"$(OUTDIR)\mod_reflector.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Debug\mod_reflector.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_reflector.so"
+ if exist .\Debug\mod_reflector.so.manifest mt.exe -manifest .\Debug\mod_reflector.so.manifest -outputresource:.\Debug\mod_reflector.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+
+!IF "$(NO_EXTERNAL_DEPS)" != "1"
+!IF EXISTS("mod_reflector.dep")
+!INCLUDE "mod_reflector.dep"
+!ELSE
+!MESSAGE Warning: cannot find "mod_reflector.dep"
+!ENDIF
+!ENDIF
+
+
+!IF "$(CFG)" == "mod_reflector - Win32 Release" || "$(CFG)" == "mod_reflector - Win32 Debug"
+
+!IF "$(CFG)" == "mod_reflector - Win32 Release"
+
+"libapr - Win32 Release" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_reflector - Win32 Debug"
+
+"libapr - Win32 Debug" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_reflector - Win32 Release"
+
+"libaprutil - Win32 Release" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_reflector - Win32 Debug"
+
+"libaprutil - Win32 Debug" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_reflector - Win32 Release"
+
+"libhttpd - Win32 Release" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 ReleaseCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_reflector - Win32 Debug"
+
+"libhttpd - Win32 Debug" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 DebugCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ENDIF
+
+SOURCE=..\..\build\win32\httpd.rc
+
+!IF "$(CFG)" == "mod_reflector - Win32 Release"
+
+
+"$(INTDIR)\mod_reflector.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_reflector.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_reflector.so" /d LONG_NAME="reflector_module for Apache" $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "mod_reflector - Win32 Debug"
+
+
+"$(INTDIR)\mod_reflector.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_reflector.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_reflector.so" /d LONG_NAME="reflector_module for Apache" $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\mod_reflector.c
+
+"$(INTDIR)\mod_reflector.obj" : $(SOURCE) "$(INTDIR)"
+
+
+
+!ENDIF
+
diff --git a/modules/filters/mod_reqtimeout.c b/modules/filters/mod_reqtimeout.c
new file mode 100644
index 0000000..0ebd78a
--- /dev/null
+++ b/modules/filters/mod_reqtimeout.c
@@ -0,0 +1,670 @@
+/* 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.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_connection.h"
+#include "http_protocol.h"
+#include "http_log.h"
+#include "http_core.h"
+#include "util_filter.h"
+#define APR_WANT_STRFUNC
+#include "apr_strings.h"
+#include "apr_version.h"
+
+module AP_MODULE_DECLARE_DATA reqtimeout_module;
+
+#define UNSET -1
+#define MRT_DEFAULT_handshake_TIMEOUT 0 /* disabled */
+#define MRT_DEFAULT_handshake_MAX_TIMEOUT 0
+#define MRT_DEFAULT_handshake_MIN_RATE 0
+#define MRT_DEFAULT_header_TIMEOUT 20
+#define MRT_DEFAULT_header_MAX_TIMEOUT 40
+#define MRT_DEFAULT_header_MIN_RATE 500
+#define MRT_DEFAULT_body_TIMEOUT 20
+#define MRT_DEFAULT_body_MAX_TIMEOUT 0
+#define MRT_DEFAULT_body_MIN_RATE 500
+
+typedef struct
+{
+ int timeout; /* timeout in secs */
+ int max_timeout; /* max timeout in secs */
+ int min_rate; /* min rate in bytes/s */
+ apr_time_t rate_factor; /* scale factor (#usecs per min_rate) */
+} reqtimeout_stage_t;
+
+typedef struct
+{
+ reqtimeout_stage_t handshake; /* Handshaking (TLS) */
+ reqtimeout_stage_t header; /* Reading the HTTP header */
+ reqtimeout_stage_t body; /* Reading the HTTP body */
+} reqtimeout_srv_cfg;
+
+/* this struct is used both as conn_config and as filter context */
+typedef struct
+{
+ apr_time_t timeout_at;
+ apr_time_t max_timeout_at;
+ reqtimeout_stage_t cur_stage;
+ int in_keep_alive;
+ char *type;
+ apr_socket_t *socket;
+ apr_bucket_brigade *tmpbb;
+} reqtimeout_con_cfg;
+
+static const char *const reqtimeout_filter_name = "reqtimeout";
+static int default_handshake_rate_factor;
+static int default_header_rate_factor;
+static int default_body_rate_factor;
+
+static void extend_timeout(reqtimeout_con_cfg *ccfg, apr_bucket_brigade *bb)
+{
+ apr_off_t len;
+ apr_time_t new_timeout_at;
+
+ if (apr_brigade_length(bb, 0, &len) != APR_SUCCESS || len <= 0)
+ return;
+
+ new_timeout_at = ccfg->timeout_at + len * ccfg->cur_stage.rate_factor;
+ if (ccfg->max_timeout_at > 0 && new_timeout_at > ccfg->max_timeout_at) {
+ ccfg->timeout_at = ccfg->max_timeout_at;
+ }
+ else {
+ ccfg->timeout_at = new_timeout_at;
+ }
+}
+
+static apr_status_t check_time_left(reqtimeout_con_cfg *ccfg,
+ apr_time_t *time_left_p,
+ apr_time_t now)
+{
+ if (!now)
+ now = apr_time_now();
+ *time_left_p = ccfg->timeout_at - now;
+ if (*time_left_p <= 0)
+ return APR_TIMEUP;
+
+ if (*time_left_p < apr_time_from_sec(1)) {
+ *time_left_p = apr_time_from_sec(1);
+ }
+ return APR_SUCCESS;
+}
+
+static apr_status_t have_lf_or_eos(apr_bucket_brigade *bb)
+{
+ apr_bucket *b = APR_BRIGADE_LAST(bb);
+
+ for ( ; b != APR_BRIGADE_SENTINEL(bb) ; b = APR_BUCKET_PREV(b) ) {
+ const char *str;
+ apr_size_t len;
+ apr_status_t rv;
+
+ if (APR_BUCKET_IS_EOS(b))
+ return APR_SUCCESS;
+
+ if (APR_BUCKET_IS_METADATA(b))
+ continue;
+
+ rv = apr_bucket_read(b, &str, &len, APR_BLOCK_READ);
+ if (rv != APR_SUCCESS)
+ return rv;
+
+ if (len == 0)
+ continue;
+
+ if (str[len-1] == APR_ASCII_LF)
+ return APR_SUCCESS;
+ }
+ return APR_INCOMPLETE;
+}
+
+/*
+ * Append bbIn to bbOut and merge small buckets, to avoid DoS by high memory
+ * usage
+ */
+static apr_status_t brigade_append(apr_bucket_brigade *bbOut, apr_bucket_brigade *bbIn)
+{
+ while (!APR_BRIGADE_EMPTY(bbIn)) {
+ apr_bucket *e = APR_BRIGADE_FIRST(bbIn);
+ const char *str;
+ apr_size_t len;
+ apr_status_t rv;
+
+ rv = apr_bucket_read(e, &str, &len, APR_BLOCK_READ);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ APR_BUCKET_REMOVE(e);
+ if (APR_BUCKET_IS_METADATA(e) || len > APR_BUCKET_BUFF_SIZE/4) {
+ APR_BRIGADE_INSERT_TAIL(bbOut, e);
+ }
+ else {
+ if (len > 0) {
+ rv = apr_brigade_write(bbOut, NULL, NULL, str, len);
+ if (rv != APR_SUCCESS) {
+ apr_bucket_destroy(e);
+ return rv;
+ }
+ }
+ apr_bucket_destroy(e);
+ }
+ }
+ return APR_SUCCESS;
+}
+
+
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+static apr_status_t reqtimeout_filter(ap_filter_t *f,
+ apr_bucket_brigade *bb,
+ ap_input_mode_t mode,
+ apr_read_type_e block,
+ apr_off_t readbytes)
+{
+ apr_time_t time_left;
+ apr_time_t now = 0;
+ apr_status_t rv;
+ apr_interval_time_t saved_sock_timeout = UNSET;
+ reqtimeout_con_cfg *ccfg = f->ctx;
+
+ if (ccfg->in_keep_alive) {
+ /* For this read[_request line()], wait for the first byte using the
+ * normal keep-alive timeout (hence don't take this expected idle time
+ * into account to setup the connection expiry below).
+ */
+ ccfg->in_keep_alive = 0;
+ rv = ap_get_brigade(f->next, bb, AP_MODE_SPECULATIVE, block, 1);
+ if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(bb)) {
+ return rv;
+ }
+ apr_brigade_cleanup(bb);
+ }
+
+ if (ccfg->cur_stage.timeout > 0) {
+ /* set new timeout */
+ now = apr_time_now();
+ ccfg->timeout_at = now + apr_time_from_sec(ccfg->cur_stage.timeout);
+ ccfg->cur_stage.timeout = 0;
+ if (ccfg->cur_stage.max_timeout > 0) {
+ ccfg->max_timeout_at = now + apr_time_from_sec(ccfg->cur_stage.max_timeout);
+ ccfg->cur_stage.max_timeout = 0;
+ }
+ }
+ else if (ccfg->timeout_at == 0) {
+ /* no timeout set, or in between requests */
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
+
+ if (!ccfg->socket) {
+ ccfg->socket = ap_get_conn_socket(f->c);
+ }
+
+ rv = check_time_left(ccfg, &time_left, now);
+ if (rv != APR_SUCCESS)
+ goto out;
+
+ if (block == APR_NONBLOCK_READ || mode == AP_MODE_INIT
+ || mode == AP_MODE_EATCRLF) {
+ rv = ap_get_brigade(f->next, bb, mode, block, readbytes);
+ if (ccfg->cur_stage.rate_factor && rv == APR_SUCCESS) {
+ extend_timeout(ccfg, bb);
+ }
+ return rv;
+ }
+
+ rv = apr_socket_timeout_get(ccfg->socket, &saved_sock_timeout);
+ AP_DEBUG_ASSERT(rv == APR_SUCCESS);
+
+ rv = apr_socket_timeout_set(ccfg->socket, MIN(time_left, saved_sock_timeout));
+ AP_DEBUG_ASSERT(rv == APR_SUCCESS);
+
+ if (mode == AP_MODE_GETLINE) {
+ /*
+ * For a blocking AP_MODE_GETLINE read, apr_brigade_split_line()
+ * would loop until a whole line has been read. As this would make it
+ * impossible to enforce a total timeout, we only do non-blocking
+ * reads.
+ */
+ apr_off_t remaining = HUGE_STRING_LEN;
+ do {
+ apr_off_t bblen;
+#if APR_MAJOR_VERSION < 2
+ apr_int32_t nsds;
+ apr_interval_time_t poll_timeout;
+ apr_pollfd_t pollset;
+#endif
+
+ rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE, APR_NONBLOCK_READ, remaining);
+ if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) {
+ break;
+ }
+
+ if (!APR_BRIGADE_EMPTY(bb)) {
+ if (ccfg->cur_stage.rate_factor) {
+ extend_timeout(ccfg, bb);
+ }
+
+ rv = have_lf_or_eos(bb);
+ if (rv != APR_INCOMPLETE) {
+ break;
+ }
+
+ rv = apr_brigade_length(bb, 1, &bblen);
+ if (rv != APR_SUCCESS) {
+ break;
+ }
+ remaining -= bblen;
+ if (remaining <= 0) {
+ break;
+ }
+
+ /* Haven't got a whole line yet, save what we have ... */
+ if (!ccfg->tmpbb) {
+ ccfg->tmpbb = apr_brigade_create(f->c->pool, f->c->bucket_alloc);
+ }
+ rv = brigade_append(ccfg->tmpbb, bb);
+ if (rv != APR_SUCCESS)
+ break;
+ }
+
+ /* ... and wait for more */
+#if APR_MAJOR_VERSION < 2
+ pollset.p = f->c->pool;
+ pollset.desc_type = APR_POLL_SOCKET;
+ pollset.reqevents = APR_POLLIN|APR_POLLHUP;
+ pollset.desc.s = ccfg->socket;
+ apr_socket_timeout_get(ccfg->socket, &poll_timeout);
+ rv = apr_poll(&pollset, 1, &nsds, poll_timeout);
+#else
+ rv = apr_socket_wait(ccfg->socket, APR_WAIT_READ);
+#endif
+ if (rv != APR_SUCCESS)
+ break;
+
+ rv = check_time_left(ccfg, &time_left, 0);
+ if (rv != APR_SUCCESS)
+ break;
+
+ rv = apr_socket_timeout_set(ccfg->socket,
+ MIN(time_left, saved_sock_timeout));
+ AP_DEBUG_ASSERT(rv == APR_SUCCESS);
+
+ } while (1);
+
+ if (ccfg->tmpbb)
+ APR_BRIGADE_PREPEND(bb, ccfg->tmpbb);
+
+ }
+ else { /* mode != AP_MODE_GETLINE */
+ rv = ap_get_brigade(f->next, bb, mode, block, readbytes);
+ /* Don't extend the timeout in speculative mode, wait for
+ * the real (relevant) bytes to be asked later, within the
+ * currently allotted time.
+ */
+ if (ccfg->cur_stage.rate_factor && rv == APR_SUCCESS
+ && mode != AP_MODE_SPECULATIVE) {
+ extend_timeout(ccfg, bb);
+ }
+ }
+
+ apr_socket_timeout_set(ccfg->socket, saved_sock_timeout);
+
+out:
+ if (APR_STATUS_IS_TIMEUP(rv)) {
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c, APLOGNO(01382)
+ "Request %s read timeout", ccfg->type);
+ /*
+ * If we allow a normal lingering close, the client may keep this
+ * process/thread busy for another 30s (MAX_SECS_TO_LINGER).
+ * Therefore we tell ap_lingering_close() to shorten this period to
+ * 2s (SECONDS_TO_LINGER).
+ */
+ apr_table_setn(f->c->notes, "short-lingering-close", "1");
+
+ /*
+ * Also, we must not allow keep-alive requests, as
+ * ap_finalize_protocol() may ignore our error status (if the timeout
+ * happened on a request body that is discarded).
+ */
+ f->c->keepalive = AP_CONN_CLOSE;
+ }
+ return rv;
+}
+
+static apr_status_t reqtimeout_eor(ap_filter_t *f, apr_bucket_brigade *bb)
+{
+ if (!APR_BRIGADE_EMPTY(bb) && AP_BUCKET_IS_EOR(APR_BRIGADE_LAST(bb))) {
+ reqtimeout_con_cfg *ccfg = f->ctx;
+ ccfg->timeout_at = 0;
+ }
+ return ap_pass_brigade(f->next, bb);
+}
+
+#define INIT_STAGE(cfg, ccfg, stage) do { \
+ if (cfg->stage.timeout != UNSET) { \
+ ccfg->cur_stage.timeout = cfg->stage.timeout; \
+ ccfg->cur_stage.max_timeout = cfg->stage.max_timeout; \
+ ccfg->cur_stage.rate_factor = cfg->stage.rate_factor; \
+ } \
+ else { \
+ ccfg->cur_stage.timeout = MRT_DEFAULT_##stage##_TIMEOUT; \
+ ccfg->cur_stage.max_timeout = MRT_DEFAULT_##stage##_MAX_TIMEOUT; \
+ ccfg->cur_stage.rate_factor = default_##stage##_rate_factor; \
+ } \
+} while (0)
+
+static int reqtimeout_init(conn_rec *c)
+{
+ reqtimeout_con_cfg *ccfg;
+ reqtimeout_srv_cfg *cfg;
+
+ cfg = ap_get_module_config(c->base_server->module_config,
+ &reqtimeout_module);
+ AP_DEBUG_ASSERT(cfg != NULL);
+
+ /* For compatibility, handshake timeout is disabled when UNSET (< 0) */
+ if (cfg->handshake.timeout <= 0
+ && cfg->header.timeout == 0
+ && cfg->body.timeout == 0) {
+ /* disabled for this vhost */
+ return DECLINED;
+ }
+
+ ccfg = ap_get_module_config(c->conn_config, &reqtimeout_module);
+ if (ccfg == NULL) {
+ ccfg = apr_pcalloc(c->pool, sizeof(reqtimeout_con_cfg));
+ ap_set_module_config(c->conn_config, &reqtimeout_module, ccfg);
+ ap_add_output_filter(reqtimeout_filter_name, ccfg, NULL, c);
+ ap_add_input_filter(reqtimeout_filter_name, ccfg, NULL, c);
+
+ ccfg->type = "handshake";
+ if (cfg->handshake.timeout > 0) {
+ INIT_STAGE(cfg, ccfg, handshake);
+ }
+ }
+
+ /* we are not handling the connection, we just do initialization */
+ return DECLINED;
+}
+
+static void reqtimeout_before_header(request_rec *r, conn_rec *c)
+{
+ reqtimeout_srv_cfg *cfg;
+ reqtimeout_con_cfg *ccfg =
+ ap_get_module_config(c->conn_config, &reqtimeout_module);
+
+ if (ccfg == NULL) {
+ /* not configured for this connection */
+ return;
+ }
+
+ cfg = ap_get_module_config(c->base_server->module_config,
+ &reqtimeout_module);
+ AP_DEBUG_ASSERT(cfg != NULL);
+
+ /* (Re)set the state for this new request, but ccfg->socket and
+ * ccfg->tmpbb which have the lifetime of the connection.
+ */
+ ccfg->type = "header";
+ ccfg->timeout_at = 0;
+ ccfg->max_timeout_at = 0;
+ ccfg->in_keep_alive = (c->keepalives > 0);
+ INIT_STAGE(cfg, ccfg, header);
+}
+
+static int reqtimeout_before_body(request_rec *r)
+{
+ reqtimeout_srv_cfg *cfg;
+ reqtimeout_con_cfg *ccfg =
+ ap_get_module_config(r->connection->conn_config, &reqtimeout_module);
+
+ if (ccfg == NULL) {
+ /* not configured for this connection */
+ return OK;
+ }
+ cfg = ap_get_module_config(r->server->module_config,
+ &reqtimeout_module);
+ AP_DEBUG_ASSERT(cfg != NULL);
+
+ ccfg->type = "body";
+ ccfg->timeout_at = 0;
+ ccfg->max_timeout_at = 0;
+ if (r->method_number == M_CONNECT) {
+ /* disabled for a CONNECT request */
+ ccfg->cur_stage.timeout = 0;
+ }
+ else {
+ INIT_STAGE(cfg, ccfg, body);
+ }
+ return OK;
+}
+
+#define UNSET_STAGE(cfg, stage) do { \
+ cfg->stage.timeout = UNSET; \
+ cfg->stage.max_timeout = UNSET; \
+ cfg->stage.min_rate = UNSET; \
+} while (0)
+
+static void *reqtimeout_create_srv_config(apr_pool_t *p, server_rec *s)
+{
+ reqtimeout_srv_cfg *cfg = apr_pcalloc(p, sizeof(reqtimeout_srv_cfg));
+
+ UNSET_STAGE(cfg, handshake);
+ UNSET_STAGE(cfg, header);
+ UNSET_STAGE(cfg, body);
+
+ return cfg;
+}
+
+#define MERGE_INT(cfg, base, add, val) \
+ cfg->val = (add->val == UNSET) ? base->val : add->val
+#define MERGE_STAGE(cfg, base, add, stage) do { \
+ MERGE_INT(cfg, base, add, stage.timeout); \
+ MERGE_INT(cfg, base, add, stage.max_timeout); \
+ MERGE_INT(cfg, base, add, stage.min_rate); \
+ cfg->stage.rate_factor = (cfg->stage.min_rate == UNSET) \
+ ? base->stage.rate_factor \
+ : add->stage.rate_factor; \
+} while (0)
+
+static void *reqtimeout_merge_srv_config(apr_pool_t *p, void *base_, void *add_)
+{
+ reqtimeout_srv_cfg *base = base_;
+ reqtimeout_srv_cfg *add = add_;
+ reqtimeout_srv_cfg *cfg = apr_pcalloc(p, sizeof(reqtimeout_srv_cfg));
+
+ MERGE_STAGE(cfg, base, add, handshake);
+ MERGE_STAGE(cfg, base, add, header);
+ MERGE_STAGE(cfg, base, add, body);
+
+ return cfg;
+}
+
+static const char *parse_int(apr_pool_t *p, const char *arg, int *val)
+{
+ char *endptr;
+ *val = strtol(arg, &endptr, 10);
+
+ if (arg == endptr) {
+ return apr_psprintf(p, "Value '%s' not numerical", endptr);
+ }
+ if (*endptr != '\0') {
+ return apr_psprintf(p, "Cannot parse '%s'", endptr);
+ }
+ if (*val < 0) {
+ return "Value must be non-negative";
+ }
+ return NULL;
+}
+
+static const char *set_reqtimeout_param(reqtimeout_srv_cfg *conf,
+ apr_pool_t *p,
+ const char *key,
+ const char *val)
+{
+ const char *ret = NULL;
+ char *rate_str = NULL, *initial_str, *max_str = NULL;
+ reqtimeout_stage_t *stage;
+
+ if (!strcasecmp(key, "handshake")) {
+ stage = &conf->handshake;
+ }
+ else if (!strcasecmp(key, "header")) {
+ stage = &conf->header;
+ }
+ else if (!strcasecmp(key, "body")) {
+ stage = &conf->body;
+ }
+ else {
+ return "Unknown RequestReadTimeout parameter";
+ }
+
+ memset(stage, 0, sizeof(*stage));
+
+ if ((rate_str = ap_strcasestr(val, ",minrate="))) {
+ initial_str = apr_pstrndup(p, val, rate_str - val);
+ rate_str += strlen(",minrate=");
+ ret = parse_int(p, rate_str, &stage->min_rate);
+ if (ret)
+ return ret;
+
+ if (stage->min_rate == 0)
+ return "Minimum data rate must be larger than 0";
+
+ if ((max_str = strchr(initial_str, '-'))) {
+ *max_str++ = '\0';
+ ret = parse_int(p, max_str, &stage->max_timeout);
+ if (ret)
+ return ret;
+ }
+
+ ret = parse_int(p, initial_str, &stage->timeout);
+ }
+ else {
+ if (ap_strchr_c(val, '-'))
+ return "Must set MinRate option if using timeout range";
+ ret = parse_int(p, val, &stage->timeout);
+ }
+ if (ret)
+ return ret;
+
+ if (stage->max_timeout && stage->timeout >= stage->max_timeout) {
+ return "Maximum timeout must be larger than initial timeout";
+ }
+
+ if (stage->min_rate) {
+ stage->rate_factor = apr_time_from_sec(1) / stage->min_rate;
+ }
+
+ return NULL;
+}
+
+static const char *set_reqtimeouts(cmd_parms *cmd, void *mconfig,
+ const char *arg)
+{
+ reqtimeout_srv_cfg *conf =
+ ap_get_module_config(cmd->server->module_config,
+ &reqtimeout_module);
+
+ while (*arg) {
+ char *word, *val;
+ const char *err;
+
+ word = ap_getword_conf(cmd->temp_pool, &arg);
+ val = strchr(word, '=');
+ if (!val) {
+ return "Invalid RequestReadTimeout parameter. Parameter must be "
+ "in the form 'key=value'";
+ }
+ else
+ *val++ = '\0';
+
+ err = set_reqtimeout_param(conf, cmd->pool, word, val);
+
+ if (err)
+ return apr_psprintf(cmd->temp_pool, "RequestReadTimeout: %s=%s: %s",
+ word, val, err);
+ }
+
+ return NULL;
+
+}
+
+static void reqtimeout_hooks(apr_pool_t *pool)
+{
+ /*
+ * mod_ssl is AP_FTYPE_CONNECTION + 5 and mod_reqtimeout needs to
+ * be called before mod_ssl for the handshake stage to catch SSL traffic.
+ */
+ ap_register_input_filter(reqtimeout_filter_name, reqtimeout_filter, NULL,
+ AP_FTYPE_CONNECTION + 8);
+
+ /*
+ * We need to pause timeout detection in between requests, for
+ * speculative and non-blocking reads, so between each outgoing EOR
+ * and the next pre_read_request call.
+ */
+ ap_register_output_filter(reqtimeout_filter_name, reqtimeout_eor, NULL,
+ AP_FTYPE_CONNECTION);
+
+ /*
+ * mod_reqtimeout needs to be called before ap_process_http_request (which
+ * is run at APR_HOOK_REALLY_LAST) but after all other protocol modules.
+ * This ensures that it only influences normal http connections and not
+ * e.g. mod_ftp. We still process it first though, for the handshake stage
+ * to work with/before mod_ssl, but since it's disabled by default it won't
+ * influence non-HTTP modules unless configured explicitly. Also, if
+ * mod_reqtimeout used the pre_connection hook, it would be inserted on
+ * mod_proxy's backend connections, and we don't want this.
+ */
+ ap_hook_process_connection(reqtimeout_init, NULL, NULL, APR_HOOK_FIRST);
+
+ ap_hook_pre_read_request(reqtimeout_before_header, NULL, NULL,
+ APR_HOOK_MIDDLE);
+ ap_hook_post_read_request(reqtimeout_before_body, NULL, NULL,
+ APR_HOOK_MIDDLE);
+
+#if MRT_DEFAULT_handshake_MIN_RATE
+ default_handshake_rate_factor = apr_time_from_sec(1) /
+ MRT_DEFAULT_handshake_MIN_RATE;
+#endif
+#if MRT_DEFAULT_header_MIN_RATE
+ default_header_rate_factor = apr_time_from_sec(1) /
+ MRT_DEFAULT_header_MIN_RATE;
+#endif
+#if MRT_DEFAULT_body_MIN_RATE
+ default_body_rate_factor = apr_time_from_sec(1) /
+ MRT_DEFAULT_body_MIN_RATE;
+#endif
+}
+
+static const command_rec reqtimeout_cmds[] = {
+ AP_INIT_RAW_ARGS("RequestReadTimeout", set_reqtimeouts, NULL, RSRC_CONF,
+ "Set various timeout parameters for TLS handshake and/or "
+ "reading request headers and body"),
+ {NULL}
+};
+
+AP_DECLARE_MODULE(reqtimeout) = {
+ STANDARD20_MODULE_STUFF,
+ NULL, /* create per-dir config structures */
+ NULL, /* merge per-dir config structures */
+ reqtimeout_create_srv_config, /* create per-server config structures */
+ reqtimeout_merge_srv_config, /* merge per-server config structures */
+ reqtimeout_cmds, /* table of config file commands */
+ reqtimeout_hooks
+};
diff --git a/modules/filters/mod_reqtimeout.dep b/modules/filters/mod_reqtimeout.dep
new file mode 100644
index 0000000..1b7679f
--- /dev/null
+++ b/modules/filters/mod_reqtimeout.dep
@@ -0,0 +1,58 @@
+# Microsoft Developer Studio Generated Dependency File, included by mod_reqtimeout.mak
+
+..\..\build\win32\httpd.rc : \
+ "..\..\include\ap_release.h"\
+
+
+.\mod_reqtimeout.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_connection.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_version.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+
diff --git a/modules/filters/mod_reqtimeout.dsp b/modules/filters/mod_reqtimeout.dsp
new file mode 100644
index 0000000..078480d
--- /dev/null
+++ b/modules/filters/mod_reqtimeout.dsp
@@ -0,0 +1,111 @@
+# Microsoft Developer Studio Project File - Name="mod_reqtimeout" - 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_reqtimeout - 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_reqtimeout.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_reqtimeout.mak" CFG="mod_reqtimeout - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_reqtimeout - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_reqtimeout - 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_reqtimeout - 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 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "AP_RL_DECLARE_EXPORT" /Fd"Release\mod_reqtimeout_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 /fo"Release/mod_reqtimeout.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_reqtimeout.so" /d LONG_NAME="reqtimeout_module for Apache"
+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_reqtimeout.so" /base:@..\..\os\win32\BaseAddr.ref,mod_reqtimeout.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_reqtimeout.so" /base:@..\..\os\win32\BaseAddr.ref,mod_reqtimeout.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_reqtimeout.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "mod_reqtimeout - 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" /D "AP_RL_DECLARE_EXPORT" /Fd"Debug\mod_reqtimeout_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 /fo"Debug/mod_reqtimeout.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_reqtimeout.so" /d LONG_NAME="reqtimeout_module for Apache"
+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_reqtimeout.so" /base:@..\..\os\win32\BaseAddr.ref,mod_reqtimeout.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_reqtimeout.so" /base:@..\..\os\win32\BaseAddr.ref,mod_reqtimeout.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_reqtimeout.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_reqtimeout - Win32 Release"
+# Name "mod_reqtimeout - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\mod_reqtimeout.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
diff --git a/modules/filters/mod_reqtimeout.mak b/modules/filters/mod_reqtimeout.mak
new file mode 100644
index 0000000..459272e
--- /dev/null
+++ b/modules/filters/mod_reqtimeout.mak
@@ -0,0 +1,353 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on mod_reqtimeout.dsp
+!IF "$(CFG)" == ""
+CFG=mod_reqtimeout - Win32 Release
+!MESSAGE No configuration specified. Defaulting to mod_reqtimeout - Win32 Release.
+!ENDIF
+
+!IF "$(CFG)" != "mod_reqtimeout - Win32 Release" && "$(CFG)" != "mod_reqtimeout - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!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_reqtimeout.mak" CFG="mod_reqtimeout - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_reqtimeout - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_reqtimeout - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(CFG)" == "mod_reqtimeout - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_reqtimeout.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_reqtimeout.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_reqtimeout.obj"
+ -@erase "$(INTDIR)\mod_reqtimeout.res"
+ -@erase "$(INTDIR)\mod_reqtimeout_src.idb"
+ -@erase "$(INTDIR)\mod_reqtimeout_src.pdb"
+ -@erase "$(OUTDIR)\mod_reqtimeout.exp"
+ -@erase "$(OUTDIR)\mod_reqtimeout.lib"
+ -@erase "$(OUTDIR)\mod_reqtimeout.pdb"
+ -@erase "$(OUTDIR)\mod_reqtimeout.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "AP_RL_DECLARE_EXPORT" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_reqtimeout_src" /FD /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_reqtimeout.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_reqtimeout.so" /d LONG_NAME="reqtimeout_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_reqtimeout.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_reqtimeout.pdb" /debug /out:"$(OUTDIR)\mod_reqtimeout.so" /implib:"$(OUTDIR)\mod_reqtimeout.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_reqtimeout.so /opt:ref
+LINK32_OBJS= \
+ "$(INTDIR)\mod_reqtimeout.obj" \
+ "$(INTDIR)\mod_reqtimeout.res" \
+ "..\..\srclib\apr\Release\libapr-1.lib" \
+ "..\..\srclib\apr-util\Release\libaprutil-1.lib" \
+ "..\..\Release\libhttpd.lib"
+
+"$(OUTDIR)\mod_reqtimeout.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Release\mod_reqtimeout.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_reqtimeout.so"
+ if exist .\Release\mod_reqtimeout.so.manifest mt.exe -manifest .\Release\mod_reqtimeout.so.manifest -outputresource:.\Release\mod_reqtimeout.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ELSEIF "$(CFG)" == "mod_reqtimeout - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_reqtimeout.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_reqtimeout.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_reqtimeout.obj"
+ -@erase "$(INTDIR)\mod_reqtimeout.res"
+ -@erase "$(INTDIR)\mod_reqtimeout_src.idb"
+ -@erase "$(INTDIR)\mod_reqtimeout_src.pdb"
+ -@erase "$(OUTDIR)\mod_reqtimeout.exp"
+ -@erase "$(OUTDIR)\mod_reqtimeout.lib"
+ -@erase "$(OUTDIR)\mod_reqtimeout.pdb"
+ -@erase "$(OUTDIR)\mod_reqtimeout.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "AP_RL_DECLARE_EXPORT" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_reqtimeout_src" /FD /EHsc /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_reqtimeout.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_reqtimeout.so" /d LONG_NAME="reqtimeout_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_reqtimeout.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_reqtimeout.pdb" /debug /out:"$(OUTDIR)\mod_reqtimeout.so" /implib:"$(OUTDIR)\mod_reqtimeout.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_reqtimeout.so
+LINK32_OBJS= \
+ "$(INTDIR)\mod_reqtimeout.obj" \
+ "$(INTDIR)\mod_reqtimeout.res" \
+ "..\..\srclib\apr\Debug\libapr-1.lib" \
+ "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \
+ "..\..\Debug\libhttpd.lib"
+
+"$(OUTDIR)\mod_reqtimeout.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Debug\mod_reqtimeout.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_reqtimeout.so"
+ if exist .\Debug\mod_reqtimeout.so.manifest mt.exe -manifest .\Debug\mod_reqtimeout.so.manifest -outputresource:.\Debug\mod_reqtimeout.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+
+!IF "$(NO_EXTERNAL_DEPS)" != "1"
+!IF EXISTS("mod_reqtimeout.dep")
+!INCLUDE "mod_reqtimeout.dep"
+!ELSE
+!MESSAGE Warning: cannot find "mod_reqtimeout.dep"
+!ENDIF
+!ENDIF
+
+
+!IF "$(CFG)" == "mod_reqtimeout - Win32 Release" || "$(CFG)" == "mod_reqtimeout - Win32 Debug"
+
+!IF "$(CFG)" == "mod_reqtimeout - Win32 Release"
+
+"libapr - Win32 Release" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_reqtimeout - Win32 Debug"
+
+"libapr - Win32 Debug" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_reqtimeout - Win32 Release"
+
+"libaprutil - Win32 Release" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_reqtimeout - Win32 Debug"
+
+"libaprutil - Win32 Debug" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_reqtimeout - Win32 Release"
+
+"libhttpd - Win32 Release" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 ReleaseCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_reqtimeout - Win32 Debug"
+
+"libhttpd - Win32 Debug" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 DebugCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ENDIF
+
+SOURCE=..\..\build\win32\httpd.rc
+
+!IF "$(CFG)" == "mod_reqtimeout - Win32 Release"
+
+
+"$(INTDIR)\mod_reqtimeout.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_reqtimeout.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_reqtimeout.so" /d LONG_NAME="reqtimeout_module for Apache" $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "mod_reqtimeout - Win32 Debug"
+
+
+"$(INTDIR)\mod_reqtimeout.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_reqtimeout.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_reqtimeout.so" /d LONG_NAME="reqtimeout_module for Apache" $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\mod_reqtimeout.c
+
+"$(INTDIR)\mod_reqtimeout.obj" : $(SOURCE) "$(INTDIR)"
+
+
+
+!ENDIF
+
diff --git a/modules/filters/mod_request.c b/modules/filters/mod_request.c
new file mode 100644
index 0000000..1768edc
--- /dev/null
+++ b/modules/filters/mod_request.c
@@ -0,0 +1,393 @@
+/* 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_request.c --- HTTP routines to set aside or process request bodies.
+ */
+
+#include "apr.h"
+#include "apr_strings.h"
+#include "apr_buckets.h"
+#include "apr_lib.h"
+
+#include "ap_config.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_protocol.h"
+#include "http_log.h" /* For errors detected in basic auth common
+ * support code... */
+#include "http_request.h"
+
+#include "mod_request.h"
+
+/* Handles for core filters */
+static ap_filter_rec_t *keep_body_input_filter_handle;
+static ap_filter_rec_t *kept_body_input_filter_handle;
+
+static apr_status_t bail_out_on_error(apr_bucket_brigade *bb,
+ ap_filter_t *f,
+ int http_error)
+{
+ apr_bucket *e;
+
+ apr_brigade_cleanup(bb);
+ e = ap_bucket_error_create(http_error,
+ NULL, f->r->pool,
+ f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, e);
+ e = apr_bucket_eos_create(f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, e);
+ return ap_pass_brigade(f->r->output_filters, bb);
+}
+
+typedef struct keep_body_filter_ctx {
+ apr_off_t remaining;
+ apr_off_t keep_body;
+} keep_body_ctx_t;
+
+/**
+ * This is the KEEP_BODY_INPUT filter for HTTP requests, for times when the
+ * body should be set aside for future use by other modules.
+ */
+static apr_status_t keep_body_filter(ap_filter_t *f, apr_bucket_brigade *b,
+ ap_input_mode_t mode,
+ apr_read_type_e block,
+ apr_off_t readbytes)
+{
+ apr_bucket *e;
+ keep_body_ctx_t *ctx = f->ctx;
+ apr_status_t rv;
+ apr_bucket *bucket;
+ apr_off_t len = 0;
+
+ if (!ctx) {
+ const char *lenp;
+ request_dir_conf *dconf = ap_get_module_config(f->r->per_dir_config,
+ &request_module);
+
+ /* must we step out of the way? */
+ if (!dconf->keep_body || f->r->kept_body) {
+ ap_remove_input_filter(f);
+ return ap_get_brigade(f->next, b, mode, block, readbytes);
+ }
+
+ f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
+
+ /* fail fast if the content length exceeds keep body */
+ lenp = apr_table_get(f->r->headers_in, "Content-Length");
+ if (lenp) {
+
+ /* Protects against over/underflow, non-digit chars in the
+ * string, leading plus/minus signs, trailing characters and
+ * a negative number.
+ */
+ if (!ap_parse_strict_length(&ctx->remaining, lenp)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01411)
+ "Invalid Content-Length '%s'", lenp);
+
+ ap_remove_input_filter(f);
+ return bail_out_on_error(b, f, HTTP_REQUEST_ENTITY_TOO_LARGE);
+ }
+
+ /* If we have a limit in effect and we know the C-L ahead of
+ * time, stop it here if it is invalid.
+ */
+ if (dconf->keep_body < ctx->remaining) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01412)
+ "Requested content-length of %" APR_OFF_T_FMT
+ " is larger than the configured limit"
+ " of %" APR_OFF_T_FMT, ctx->remaining, dconf->keep_body);
+ ap_remove_input_filter(f);
+ return bail_out_on_error(b, f, HTTP_REQUEST_ENTITY_TOO_LARGE);
+ }
+
+ }
+
+ f->r->kept_body = apr_brigade_create(f->r->pool, f->r->connection->bucket_alloc);
+ ctx->remaining = dconf->keep_body;
+ }
+
+ /* get the brigade from upstream, and read it in to get its length */
+ rv = ap_get_brigade(f->next, b, mode, block, readbytes);
+ if (rv == APR_SUCCESS) {
+ rv = apr_brigade_length(b, 1, &len);
+ }
+
+ /* does the length take us over the limit? */
+ if (APR_SUCCESS == rv && len > ctx->remaining) {
+ if (f->r->kept_body) {
+ apr_brigade_cleanup(f->r->kept_body);
+ f->r->kept_body = NULL;
+ }
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01413)
+ "Requested content-length of %" APR_OFF_T_FMT
+ " is larger than the configured limit"
+ " of %" APR_OFF_T_FMT, len, ctx->keep_body);
+ return bail_out_on_error(b, f, HTTP_REQUEST_ENTITY_TOO_LARGE);
+ }
+ ctx->remaining -= len;
+
+ /* pass any errors downstream */
+ if (rv != APR_SUCCESS) {
+ if (f->r->kept_body) {
+ apr_brigade_cleanup(f->r->kept_body);
+ f->r->kept_body = NULL;
+ }
+ return rv;
+ }
+
+ /* all is well, set aside the buckets */
+ for (bucket = APR_BRIGADE_FIRST(b);
+ bucket != APR_BRIGADE_SENTINEL(b);
+ bucket = APR_BUCKET_NEXT(bucket))
+ {
+ apr_bucket_copy(bucket, &e);
+ APR_BRIGADE_INSERT_TAIL(f->r->kept_body, e);
+ }
+
+ return APR_SUCCESS;
+}
+
+
+typedef struct kept_body_filter_ctx {
+ apr_off_t offset;
+ apr_off_t remaining;
+} kept_body_ctx_t;
+
+/**
+ * Initialisation of filter to handle a kept body on subrequests.
+ *
+ * If a body is to be reinserted into a subrequest, any chunking will have
+ * been removed from the body during storage. We need to change the request
+ * from Transfer-Encoding: chunked to an explicit Content-Length.
+ */
+static int kept_body_filter_init(ap_filter_t *f)
+{
+ apr_off_t length = 0;
+ request_rec *r = f->r;
+ apr_bucket_brigade *kept_body = r->kept_body;
+
+ if (kept_body) {
+ apr_table_unset(r->headers_in, "Transfer-Encoding");
+ apr_brigade_length(kept_body, 1, &length);
+ apr_table_setn(r->headers_in, "Content-Length", apr_off_t_toa(r->pool, length));
+ }
+
+ return OK;
+}
+
+/**
+ * Filter to handle a kept body on subrequests.
+ *
+ * If a body has been previously kept by the request, and if a subrequest wants
+ * to re-insert the body into the request, this input filter makes it happen.
+ */
+static apr_status_t kept_body_filter(ap_filter_t *f, apr_bucket_brigade *b,
+ ap_input_mode_t mode,
+ apr_read_type_e block,
+ apr_off_t readbytes)
+{
+ request_rec *r = f->r;
+ apr_bucket_brigade *kept_body = r->kept_body;
+ kept_body_ctx_t *ctx = f->ctx;
+ apr_bucket *ec, *e2;
+ apr_status_t rv;
+
+ /* just get out of the way of things we don't want. */
+ if (!kept_body || (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE)) {
+ return ap_get_brigade(f->next, b, mode, block, readbytes);
+ }
+
+ /* set up the context if it does not already exist */
+ if (!ctx) {
+ f->ctx = ctx = apr_palloc(f->r->pool, sizeof(*ctx));
+ ctx->offset = 0;
+ apr_brigade_length(kept_body, 1, &ctx->remaining);
+ }
+
+ /* kept_body is finished, send next filter */
+ if (ctx->remaining <= 0) {
+ return ap_get_brigade(f->next, b, mode, block, readbytes);
+ }
+
+ /* send all of the kept_body, but no more */
+ if (readbytes > ctx->remaining) {
+ readbytes = ctx->remaining;
+ }
+
+ /* send part of the kept_body */
+ if ((rv = apr_brigade_partition(kept_body, ctx->offset, &ec)) != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01414)
+ "apr_brigade_partition() failed on kept_body at %" APR_OFF_T_FMT, ctx->offset);
+ return rv;
+ }
+ if ((rv = apr_brigade_partition(kept_body, ctx->offset + readbytes, &e2)) != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01415)
+ "apr_brigade_partition() failed on kept_body at %" APR_OFF_T_FMT, ctx->offset + readbytes);
+ return rv;
+ }
+
+ do {
+ apr_bucket *foo;
+ const char *str;
+ apr_size_t len;
+
+ if (apr_bucket_copy(ec, &foo) != APR_SUCCESS) {
+ /* As above; this should not fail since the bucket has
+ * a known length, but just to be sure, this takes
+ * care of uncopyable buckets that do somehow manage
+ * to slip through. */
+ /* XXX: check for failure? */
+ apr_bucket_read(ec, &str, &len, APR_BLOCK_READ);
+ apr_bucket_copy(ec, &foo);
+ }
+ APR_BRIGADE_INSERT_TAIL(b, foo);
+ ec = APR_BUCKET_NEXT(ec);
+ } while (ec != e2);
+
+ ctx->remaining -= readbytes;
+ ctx->offset += readbytes;
+
+ return APR_SUCCESS;
+}
+
+/**
+ * Check whether this filter is not already present.
+ */
+static int request_is_filter_present(request_rec * r, ap_filter_rec_t *fn)
+{
+ ap_filter_t * f = r->input_filters;
+ while (f) {
+ if (f->frec == fn) {
+ return 1;
+ }
+ f = f->next;
+ }
+ return 0;
+}
+
+/**
+ * Insert filter hook.
+ *
+ * Add the KEEP_BODY filter to the request, if the admin wants to keep
+ * the body using the KeptBodySize directive.
+ *
+ * As a precaution, any pre-existing instances of either the kept_body or
+ * keep_body filters will be removed before the filter is added.
+ *
+ * @param r The request
+ */
+static void ap_request_insert_filter(request_rec * r)
+{
+ request_dir_conf *conf = ap_get_module_config(r->per_dir_config,
+ &request_module);
+
+ if (r->kept_body) {
+ if (!request_is_filter_present(r, kept_body_input_filter_handle)) {
+ ap_add_input_filter_handle(kept_body_input_filter_handle,
+ NULL, r, r->connection);
+ }
+ }
+ else if (conf->keep_body) {
+ if (!request_is_filter_present(r, kept_body_input_filter_handle)) {
+ ap_add_input_filter_handle(keep_body_input_filter_handle,
+ NULL, r, r->connection);
+ }
+ }
+}
+
+/*
+ * Remove the kept_body and keep_body filters from this specific request.
+ */
+static void ap_request_remove_filter(request_rec *r)
+{
+ ap_filter_t *f = r->input_filters;
+
+ while (f) {
+ if (f->frec->filter_func.in_func == kept_body_filter ||
+ f->frec->filter_func.in_func == keep_body_filter) {
+ ap_remove_input_filter(f);
+ }
+ f = f->next;
+ }
+}
+
+static void *create_request_dir_config(apr_pool_t *p, char *dummy)
+{
+ request_dir_conf *new =
+ (request_dir_conf *) apr_pcalloc(p, sizeof(request_dir_conf));
+
+ new->keep_body_set = 0; /* unset */
+ new->keep_body = 0; /* don't by default */
+
+ return (void *) new;
+}
+
+static void *merge_request_dir_config(apr_pool_t *p, void *basev, void *addv)
+{
+ request_dir_conf *new = (request_dir_conf *) apr_pcalloc(p, sizeof(request_dir_conf));
+ request_dir_conf *add = (request_dir_conf *) addv;
+ request_dir_conf *base = (request_dir_conf *) basev;
+
+ new->keep_body = (add->keep_body_set == 0) ? base->keep_body : add->keep_body;
+ new->keep_body_set = add->keep_body_set || base->keep_body_set;
+
+ return new;
+}
+
+static const char *set_kept_body_size(cmd_parms *cmd, void *dconf,
+ const char *arg)
+{
+ request_dir_conf *conf = dconf;
+ char *end = NULL;
+
+ if (APR_SUCCESS != apr_strtoff(&(conf->keep_body), arg, &end, 10)
+ || conf->keep_body < 0 || *end) {
+ return "KeptBodySize must be a valid size in bytes, or zero.";
+ }
+ conf->keep_body_set = 1;
+
+ return NULL;
+}
+
+static const command_rec request_cmds[] = {
+ AP_INIT_TAKE1("KeptBodySize", set_kept_body_size, NULL, ACCESS_CONF,
+ "Maximum size of request bodies kept aside for use by filters"),
+ { NULL }
+};
+
+static void register_hooks(apr_pool_t *p)
+{
+ keep_body_input_filter_handle =
+ ap_register_input_filter(KEEP_BODY_FILTER, keep_body_filter,
+ NULL, AP_FTYPE_RESOURCE);
+ kept_body_input_filter_handle =
+ ap_register_input_filter(KEPT_BODY_FILTER, kept_body_filter,
+ kept_body_filter_init, AP_FTYPE_RESOURCE);
+ ap_hook_insert_filter(ap_request_insert_filter, NULL, NULL, APR_HOOK_LAST);
+ APR_REGISTER_OPTIONAL_FN(ap_request_insert_filter);
+ APR_REGISTER_OPTIONAL_FN(ap_request_remove_filter);
+}
+
+AP_DECLARE_MODULE(request) = {
+ STANDARD20_MODULE_STUFF,
+ create_request_dir_config, /* create per-directory config structure */
+ merge_request_dir_config, /* merge per-directory config structures */
+ NULL, /* create per-server config structure */
+ NULL, /* merge per-server config structures */
+ request_cmds, /* command apr_table_t */
+ register_hooks /* register hooks */
+};
diff --git a/modules/filters/mod_request.dep b/modules/filters/mod_request.dep
new file mode 100644
index 0000000..ae332a7
--- /dev/null
+++ b/modules/filters/mod_request.dep
@@ -0,0 +1,55 @@
+# Microsoft Developer Studio Generated Dependency File, included by mod_request.mak
+
+..\..\build\win32\httpd.rc : \
+ "..\..\include\ap_release.h"\
+
+
+.\mod_request.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\mod_request.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+
diff --git a/modules/filters/mod_request.dsp b/modules/filters/mod_request.dsp
new file mode 100644
index 0000000..4032e00
--- /dev/null
+++ b/modules/filters/mod_request.dsp
@@ -0,0 +1,115 @@
+# Microsoft Developer Studio Project File - Name="mod_request" - 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_request - 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_request.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_request.mak" CFG="mod_request - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_request - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_request - 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_request - 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 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_request_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 /fo"Release/mod_request.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_request.so" /d LONG_NAME="request_module for Apache"
+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_request.so" /base:@..\..\os\win32\BaseAddr.ref,mod_request.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_request.so" /base:@..\..\os\win32\BaseAddr.ref,mod_request.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_request.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "mod_request - 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_request_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 /fo"Debug/mod_request.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_request.so" /d LONG_NAME="request_module for Apache"
+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_request.so" /base:@..\..\os\win32\BaseAddr.ref,mod_request.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_request.so" /base:@..\..\os\win32\BaseAddr.ref,mod_request.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_request.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_request - Win32 Release"
+# Name "mod_request - Win32 Debug"
+# Begin Source File
+
+SOURCE=..\..\include\mod_request.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\mod_request.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
diff --git a/modules/filters/mod_request.mak b/modules/filters/mod_request.mak
new file mode 100644
index 0000000..7ceb603
--- /dev/null
+++ b/modules/filters/mod_request.mak
@@ -0,0 +1,353 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on mod_request.dsp
+!IF "$(CFG)" == ""
+CFG=mod_request - Win32 Release
+!MESSAGE No configuration specified. Defaulting to mod_request - Win32 Release.
+!ENDIF
+
+!IF "$(CFG)" != "mod_request - Win32 Release" && "$(CFG)" != "mod_request - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!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_request.mak" CFG="mod_request - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_request - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_request - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(CFG)" == "mod_request - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_request.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_request.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_request.obj"
+ -@erase "$(INTDIR)\mod_request.res"
+ -@erase "$(INTDIR)\mod_request_src.idb"
+ -@erase "$(INTDIR)\mod_request_src.pdb"
+ -@erase "$(OUTDIR)\mod_request.exp"
+ -@erase "$(OUTDIR)\mod_request.lib"
+ -@erase "$(OUTDIR)\mod_request.pdb"
+ -@erase "$(OUTDIR)\mod_request.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_request_src" /FD /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_request.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_request.so" /d LONG_NAME="request_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_request.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_request.pdb" /debug /out:"$(OUTDIR)\mod_request.so" /implib:"$(OUTDIR)\mod_request.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_request.so /opt:ref
+LINK32_OBJS= \
+ "$(INTDIR)\mod_request.obj" \
+ "$(INTDIR)\mod_request.res" \
+ "..\..\srclib\apr\Release\libapr-1.lib" \
+ "..\..\srclib\apr-util\Release\libaprutil-1.lib" \
+ "..\..\Release\libhttpd.lib"
+
+"$(OUTDIR)\mod_request.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Release\mod_request.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_request.so"
+ if exist .\Release\mod_request.so.manifest mt.exe -manifest .\Release\mod_request.so.manifest -outputresource:.\Release\mod_request.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ELSEIF "$(CFG)" == "mod_request - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_request.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_request.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_request.obj"
+ -@erase "$(INTDIR)\mod_request.res"
+ -@erase "$(INTDIR)\mod_request_src.idb"
+ -@erase "$(INTDIR)\mod_request_src.pdb"
+ -@erase "$(OUTDIR)\mod_request.exp"
+ -@erase "$(OUTDIR)\mod_request.lib"
+ -@erase "$(OUTDIR)\mod_request.pdb"
+ -@erase "$(OUTDIR)\mod_request.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_request_src" /FD /EHsc /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_request.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_request.so" /d LONG_NAME="request_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_request.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_request.pdb" /debug /out:"$(OUTDIR)\mod_request.so" /implib:"$(OUTDIR)\mod_request.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_request.so
+LINK32_OBJS= \
+ "$(INTDIR)\mod_request.obj" \
+ "$(INTDIR)\mod_request.res" \
+ "..\..\srclib\apr\Debug\libapr-1.lib" \
+ "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \
+ "..\..\Debug\libhttpd.lib"
+
+"$(OUTDIR)\mod_request.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Debug\mod_request.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_request.so"
+ if exist .\Debug\mod_request.so.manifest mt.exe -manifest .\Debug\mod_request.so.manifest -outputresource:.\Debug\mod_request.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+
+!IF "$(NO_EXTERNAL_DEPS)" != "1"
+!IF EXISTS("mod_request.dep")
+!INCLUDE "mod_request.dep"
+!ELSE
+!MESSAGE Warning: cannot find "mod_request.dep"
+!ENDIF
+!ENDIF
+
+
+!IF "$(CFG)" == "mod_request - Win32 Release" || "$(CFG)" == "mod_request - Win32 Debug"
+
+!IF "$(CFG)" == "mod_request - Win32 Release"
+
+"libapr - Win32 Release" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_request - Win32 Debug"
+
+"libapr - Win32 Debug" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_request - Win32 Release"
+
+"libaprutil - Win32 Release" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_request - Win32 Debug"
+
+"libaprutil - Win32 Debug" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_request - Win32 Release"
+
+"libhttpd - Win32 Release" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 ReleaseCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_request - Win32 Debug"
+
+"libhttpd - Win32 Debug" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 DebugCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ENDIF
+
+SOURCE=..\..\build\win32\httpd.rc
+
+!IF "$(CFG)" == "mod_request - Win32 Release"
+
+
+"$(INTDIR)\mod_request.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_request.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_request.so" /d LONG_NAME="request_module for Apache" $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "mod_request - Win32 Debug"
+
+
+"$(INTDIR)\mod_request.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_request.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_request.so" /d LONG_NAME="request_module for Apache" $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\mod_request.c
+
+"$(INTDIR)\mod_request.obj" : $(SOURCE) "$(INTDIR)"
+
+
+
+!ENDIF
+
diff --git a/modules/filters/mod_sed.c b/modules/filters/mod_sed.c
new file mode 100644
index 0000000..12cb04a
--- /dev/null
+++ b/modules/filters/mod_sed.c
@@ -0,0 +1,537 @@
+/*
+ * Copyright (c) 2005, 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed 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.
+ */
+
+#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"
+#include "libsed.h"
+
+static const char *sed_filter_name = "Sed";
+#define MODSED_OUTBUF_SIZE 8000
+#define MAX_TRANSIENT_BUCKETS 50
+
+typedef struct sed_expr_config
+{
+ sed_commands_t *sed_cmds;
+ const char *last_error;
+} sed_expr_config;
+
+typedef struct sed_config
+{
+ sed_expr_config output;
+ sed_expr_config input;
+} sed_config;
+
+/* Context for filter invocation for single HTTP request */
+typedef struct sed_filter_ctxt
+{
+ sed_eval_t eval;
+ ap_filter_t *f;
+ request_rec *r;
+ apr_bucket_brigade *bb;
+ apr_bucket_brigade *bbinp;
+ char *outbuf;
+ char *curoutbuf;
+ apr_size_t bufsize;
+ apr_pool_t *tpool;
+ int numbuckets;
+} sed_filter_ctxt;
+
+module AP_MODULE_DECLARE_DATA sed_module;
+
+/* This function will be call back from libsed functions if there is any error
+ * happened during execution of sed scripts
+ */
+static apr_status_t log_sed_errf(void *data, const char *error)
+{
+ request_rec *r = (request_rec *) data;
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02998) "%s", error);
+ return APR_SUCCESS;
+}
+
+/* This function will be call back from libsed functions if there is any
+ * compilation error.
+ */
+static apr_status_t sed_compile_errf(void *data, const char *error)
+{
+ sed_expr_config *sed_cfg = (sed_expr_config *) data;
+ sed_cfg->last_error = error;
+ return APR_SUCCESS;
+}
+
+/* clear the temporary pool (used for transient buckets)
+ */
+static void clear_ctxpool(sed_filter_ctxt* ctx)
+{
+ apr_pool_clear(ctx->tpool);
+ ctx->outbuf = NULL;
+ ctx->curoutbuf = NULL;
+ ctx->numbuckets = 0;
+}
+
+/* alloc_outbuf
+ * allocate output buffer
+ */
+static void alloc_outbuf(sed_filter_ctxt* ctx)
+{
+ ctx->outbuf = apr_palloc(ctx->tpool, ctx->bufsize + 1);
+ ctx->curoutbuf = ctx->outbuf;
+}
+
+/* append_bucket
+ * Allocate a new bucket from buf and sz and append to ctx->bb
+ */
+static apr_status_t append_bucket(sed_filter_ctxt* ctx, char* buf, apr_size_t sz)
+{
+ apr_status_t status = APR_SUCCESS;
+ apr_bucket *b;
+ if (ctx->tpool == ctx->r->pool) {
+ /* We are not using transient bucket */
+ b = apr_bucket_pool_create(buf, sz, ctx->r->pool,
+ ctx->r->connection->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
+ }
+ else {
+ /* We are using transient bucket */
+ b = apr_bucket_transient_create(buf, sz,
+ ctx->r->connection->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
+ ctx->numbuckets++;
+ if (ctx->numbuckets >= MAX_TRANSIENT_BUCKETS) {
+ b = apr_bucket_flush_create(ctx->r->connection->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
+ status = ap_pass_brigade(ctx->f->next, ctx->bb);
+ apr_brigade_cleanup(ctx->bb);
+ clear_ctxpool(ctx);
+ }
+ }
+ return status;
+}
+
+/*
+ * flush_output_buffer
+ * Flush the output data (stored in ctx->outbuf)
+ */
+static apr_status_t flush_output_buffer(sed_filter_ctxt *ctx)
+{
+ apr_size_t size = ctx->curoutbuf - ctx->outbuf;
+ char *out;
+ apr_status_t status = APR_SUCCESS;
+ if ((ctx->outbuf == NULL) || (size <=0))
+ return status;
+ out = apr_pmemdup(ctx->tpool, ctx->outbuf, size);
+ status = append_bucket(ctx, out, size);
+ ctx->curoutbuf = ctx->outbuf;
+ return status;
+}
+
+/* This is a call back function. When libsed wants to generate the output,
+ * this function will be invoked.
+ */
+static apr_status_t sed_write_output(void *dummy, char *buf, apr_size_t sz)
+{
+ /* dummy is basically filter context. Context is passed during invocation
+ * of sed_eval_buffer
+ */
+ apr_size_t remainbytes = 0;
+ apr_status_t status = APR_SUCCESS;
+ sed_filter_ctxt *ctx = (sed_filter_ctxt *) dummy;
+ if (ctx->outbuf == NULL) {
+ alloc_outbuf(ctx);
+ }
+ remainbytes = ctx->bufsize - (ctx->curoutbuf - ctx->outbuf);
+ if (sz >= remainbytes) {
+ if (remainbytes > 0) {
+ memcpy(ctx->curoutbuf, buf, remainbytes);
+ buf += remainbytes;
+ sz -= remainbytes;
+ ctx->curoutbuf += remainbytes;
+ }
+ /* buffer is now full */
+ status = append_bucket(ctx, ctx->outbuf, ctx->bufsize);
+ if (status == APR_SUCCESS) {
+ /* if size is bigger than the allocated buffer directly add to output
+ * brigade */
+ if (sz >= ctx->bufsize) {
+ char* newbuf = apr_pmemdup(ctx->tpool, buf, sz);
+ status = append_bucket(ctx, newbuf, sz);
+ if (status == APR_SUCCESS) {
+ /* old buffer is now used so allocate new buffer */
+ alloc_outbuf(ctx);
+ }
+ else {
+ clear_ctxpool(ctx);
+ }
+ }
+ else {
+ /* old buffer is now used so allocate new buffer */
+ alloc_outbuf(ctx);
+ memcpy(ctx->curoutbuf, buf, sz);
+ ctx->curoutbuf += sz;
+ }
+ }
+ else {
+ clear_ctxpool(ctx);
+ }
+ }
+ else {
+ memcpy(ctx->curoutbuf, buf, sz);
+ ctx->curoutbuf += sz;
+ }
+ return status;
+}
+
+/* Compile a sed expression. Compiled context is saved in sed_cfg->sed_cmds.
+ * Memory required for compilation context is allocated from cmd->pool.
+ */
+static apr_status_t compile_sed_expr(sed_expr_config *sed_cfg,
+ cmd_parms *cmd,
+ const char *expr)
+{
+ apr_status_t status = APR_SUCCESS;
+
+ if (!sed_cfg->sed_cmds) {
+ sed_commands_t *sed_cmds;
+ sed_cmds = apr_pcalloc(cmd->pool, sizeof(sed_commands_t));
+ status = sed_init_commands(sed_cmds, sed_compile_errf, sed_cfg,
+ cmd->pool);
+ if (status != APR_SUCCESS) {
+ sed_destroy_commands(sed_cmds);
+ return status;
+ }
+ sed_cfg->sed_cmds = sed_cmds;
+ }
+ status = sed_compile_string(sed_cfg->sed_cmds, expr);
+ if (status != APR_SUCCESS) {
+ sed_destroy_commands(sed_cfg->sed_cmds);
+ sed_cfg->sed_cmds = NULL;
+ }
+ return status;
+}
+
+/* sed eval cleanup function */
+static apr_status_t sed_eval_cleanup(void *data)
+{
+ sed_eval_t *eval = (sed_eval_t *) data;
+ sed_destroy_eval(eval);
+ return APR_SUCCESS;
+}
+
+/* Initialize sed filter context. If successful then context is set in f->ctx
+ */
+static apr_status_t init_context(ap_filter_t *f, sed_expr_config *sed_cfg, int usetpool)
+{
+ apr_status_t status;
+ sed_filter_ctxt* ctx;
+ request_rec *r = f->r;
+ /* Create the context. Call sed_init_eval. libsed will generated
+ * output by calling sed_write_output and generates any error by
+ * invoking log_sed_errf.
+ */
+ ctx = apr_pcalloc(r->pool, sizeof(sed_filter_ctxt));
+ ctx->r = r;
+ ctx->bb = NULL;
+ ctx->numbuckets = 0;
+ ctx->f = f;
+ status = sed_init_eval(&ctx->eval, sed_cfg->sed_cmds, log_sed_errf,
+ r, &sed_write_output, r->pool);
+ if (status != APR_SUCCESS) {
+ return status;
+ }
+ apr_pool_cleanup_register(r->pool, &ctx->eval, sed_eval_cleanup,
+ apr_pool_cleanup_null);
+ ctx->bufsize = MODSED_OUTBUF_SIZE;
+ if (usetpool) {
+ apr_pool_create(&(ctx->tpool), r->pool);
+ apr_pool_tag(ctx->tpool, "sed_tpool");
+ }
+ else {
+ ctx->tpool = r->pool;
+ }
+ alloc_outbuf(ctx);
+ f->ctx = ctx;
+ return APR_SUCCESS;
+}
+
+/* Entry function for Sed output filter */
+static apr_status_t sed_response_filter(ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+ apr_bucket *b;
+ apr_status_t status = APR_SUCCESS;
+ sed_config *cfg = ap_get_module_config(f->r->per_dir_config,
+ &sed_module);
+ sed_filter_ctxt *ctx = f->ctx;
+ sed_expr_config *sed_cfg = &cfg->output;
+
+ if ((sed_cfg == NULL) || (sed_cfg->sed_cmds == NULL)) {
+ /* No sed expressions */
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ if (ctx == NULL) {
+
+ if (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(bb))) {
+ /* no need to run sed filter for Head requests */
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ status = init_context(f, sed_cfg, 1);
+ if (status != APR_SUCCESS)
+ return status;
+ ctx = f->ctx;
+ apr_table_unset(f->r->headers_out, "Content-Length");
+
+ ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+ }
+
+ /* Here is the main logic. Iterate through all the buckets, read the
+ * content of the bucket, call sed_eval_buffer on the data.
+ * sed_eval_buffer will read the data line by line, run filters on each
+ * line. sed_eval_buffer will generates the output by calling
+ * sed_write_output which will add the output to ctx->bb. At the end of
+ * the loop, ctx->bb is passed to the next filter in chain. At the end of
+ * the data, if new line is not found then sed_eval_buffer will store the
+ * data in its own buffer.
+ *
+ * Once eos bucket is found then sed_finalize_eval will flush the rest of
+ * the data. If there is no new line in last line of data, new line is
+ * appended (that is a solaris sed behavior). libsed's internal memory for
+ * evaluation is allocated on request's pool so it will be cleared once
+ * request is over.
+ *
+ * If flush bucket is found then append the flush bucket to ctx->bb
+ * and pass it to next filter. There may be some data which will still be
+ * in sed's internal buffer which can't be flushed until new line
+ * character is arrived.
+ */
+ while (!APR_BRIGADE_EMPTY(bb)) {
+ b = APR_BRIGADE_FIRST(bb);
+ if (APR_BUCKET_IS_EOS(b)) {
+ /* Now clean up the internal sed buffer */
+ sed_finalize_eval(&ctx->eval, ctx);
+ status = flush_output_buffer(ctx);
+ if (status != APR_SUCCESS) {
+ break;
+ }
+ /* Move the eos bucket to ctx->bb brigade */
+ APR_BUCKET_REMOVE(b);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
+ }
+ else if (APR_BUCKET_IS_FLUSH(b)) {
+ status = flush_output_buffer(ctx);
+ if (status != APR_SUCCESS) {
+ break;
+ }
+ /* Move the flush bucket to ctx->bb brigade */
+ APR_BUCKET_REMOVE(b);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
+ }
+ else {
+ if (!APR_BUCKET_IS_METADATA(b)) {
+ const char *buf = NULL;
+ apr_size_t bytes = 0;
+
+ status = apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ);
+ if (status == APR_SUCCESS) {
+ status = sed_eval_buffer(&ctx->eval, buf, bytes, ctx);
+ }
+ if (status != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, status, f->r, APLOGNO(10394) "error evaluating sed on output");
+ break;
+ }
+ }
+ apr_bucket_delete(b);
+ }
+ }
+ if (status == APR_SUCCESS) {
+ status = flush_output_buffer(ctx);
+ }
+ if (!APR_BRIGADE_EMPTY(ctx->bb)) {
+ if (status == APR_SUCCESS) {
+ status = ap_pass_brigade(f->next, ctx->bb);
+ }
+ apr_brigade_cleanup(ctx->bb);
+ }
+ clear_ctxpool(ctx);
+ return status;
+}
+
+/* Entry function for Sed input filter */
+static apr_status_t sed_request_filter(ap_filter_t *f,
+ apr_bucket_brigade *bb,
+ ap_input_mode_t mode,
+ apr_read_type_e block,
+ apr_off_t readbytes)
+{
+ sed_config *cfg = ap_get_module_config(f->r->per_dir_config,
+ &sed_module);
+ sed_filter_ctxt *ctx = f->ctx;
+ apr_status_t status;
+ apr_bucket_brigade *bbinp;
+ sed_expr_config *sed_cfg = &cfg->input;
+
+ if (mode != AP_MODE_READBYTES) {
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
+
+ if ((sed_cfg == NULL) || (sed_cfg->sed_cmds == NULL)) {
+ /* No sed expression */
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
+
+ if (!ctx) {
+ if (!ap_is_initial_req(f->r)) {
+ ap_remove_input_filter(f);
+ /* XXX : Should we filter the sub requests too */
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
+ status = init_context(f, sed_cfg, 0);
+ if (status != APR_SUCCESS)
+ return status;
+ ctx = f->ctx;
+ ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+ ctx->bbinp = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+ }
+
+ bbinp = ctx->bbinp;
+
+ /* Here is the logic :
+ * Read the readbytes data from next level fiter into bbinp. Loop through
+ * the buckets in bbinp and read the data from buckets and invoke
+ * sed_eval_buffer on the data. libsed will generate its output using
+ * sed_write_output which will add data in ctx->bb. Do it until it have
+ * at least one bucket in ctx->bb. At the end of data eos bucket
+ * should be there.
+ *
+ * Once eos bucket is seen, then invoke sed_finalize_eval to clear the
+ * output. If the last byte of data is not a new line character then sed
+ * will add a new line to the data that is default sed behaviour. Note
+ * that using this filter with POST data, caller may not expect this
+ * behaviour.
+ *
+ * If next level fiter generate the flush bucket, we can't do much about
+ * it. If we want to return the flush bucket in brigade bb (to the caller)
+ * the question is where to add it?
+ */
+ while (APR_BRIGADE_EMPTY(ctx->bb)) {
+ apr_bucket *b;
+
+ /* read the bytes from next level filter */
+ apr_brigade_cleanup(bbinp);
+ status = ap_get_brigade(f->next, bbinp, mode, block, readbytes);
+ if (status != APR_SUCCESS) {
+ return status;
+ }
+ for (b = APR_BRIGADE_FIRST(bbinp); b != APR_BRIGADE_SENTINEL(bbinp);
+ b = APR_BUCKET_NEXT(b)) {
+ const char *buf = NULL;
+ apr_size_t bytes;
+
+ if (APR_BUCKET_IS_EOS(b)) {
+ /* eos bucket. Clear the internal sed buffers */
+ sed_finalize_eval(&ctx->eval, ctx);
+ flush_output_buffer(ctx);
+ APR_BUCKET_REMOVE(b);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
+ break;
+ }
+ else if (APR_BUCKET_IS_FLUSH(b)) {
+ /* What should we do with flush bucket */
+ continue;
+ }
+ if (apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ)
+ == APR_SUCCESS) {
+ status = sed_eval_buffer(&ctx->eval, buf, bytes, ctx);
+ if (status != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, status, f->r, APLOGNO(10395) "error evaluating sed on input");
+ return status;
+ }
+ flush_output_buffer(ctx);
+ }
+ }
+ }
+
+ if (!APR_BRIGADE_EMPTY(ctx->bb)) {
+ apr_bucket *b = NULL;
+
+ if (apr_brigade_partition(ctx->bb, readbytes, &b) == APR_INCOMPLETE) {
+ APR_BRIGADE_CONCAT(bb, ctx->bb);
+ }
+ else {
+ APR_BRIGADE_CONCAT(bb, ctx->bb);
+ apr_brigade_split_ex(bb, b, ctx->bb);
+ }
+ }
+ return APR_SUCCESS;
+}
+
+static const char *sed_add_expr(cmd_parms *cmd, void *cfg, const char *arg)
+{
+ int offset = (int) (long) cmd->info;
+ sed_expr_config *sed_cfg =
+ (sed_expr_config *) (((char *) cfg) + offset);
+ if (compile_sed_expr(sed_cfg, cmd, arg) != APR_SUCCESS) {
+ return apr_psprintf(cmd->temp_pool,
+ "Failed to compile sed expression. %s",
+ sed_cfg->last_error);
+ }
+ return NULL;
+}
+
+static void *create_sed_dir_config(apr_pool_t *p, char *s)
+{
+ sed_config *cfg = apr_pcalloc(p, sizeof(sed_config));
+ return cfg;
+}
+
+static const command_rec sed_filter_cmds[] = {
+ AP_INIT_TAKE1("OutputSed", sed_add_expr,
+ (void *) APR_OFFSETOF(sed_config, output),
+ ACCESS_CONF,
+ "Sed regular expression for Response"),
+ AP_INIT_TAKE1("InputSed", sed_add_expr,
+ (void *) APR_OFFSETOF(sed_config, input),
+ ACCESS_CONF,
+ "Sed regular expression for Request"),
+ {NULL}
+};
+
+static void register_hooks(apr_pool_t *p)
+{
+ ap_register_output_filter(sed_filter_name, sed_response_filter, NULL,
+ AP_FTYPE_RESOURCE);
+ ap_register_input_filter(sed_filter_name, sed_request_filter, NULL,
+ AP_FTYPE_RESOURCE);
+}
+
+AP_DECLARE_MODULE(sed) = {
+ STANDARD20_MODULE_STUFF,
+ create_sed_dir_config, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ sed_filter_cmds, /* command table */
+ register_hooks /* register hooks */
+};
diff --git a/modules/filters/mod_sed.dep b/modules/filters/mod_sed.dep
new file mode 100644
index 0000000..6f1ef2b
--- /dev/null
+++ b/modules/filters/mod_sed.dep
@@ -0,0 +1,109 @@
+# Microsoft Developer Studio Generated Dependency File, included by mod_sed.mak
+
+..\..\build\win32\httpd.rc : \
+ "..\..\include\ap_release.h"\
+
+
+.\mod_sed.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\libsed.h"\
+
+
+.\regexp.c : \
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\libsed.h"\
+ ".\regexp.h"\
+ ".\sed.h"\
+
+
+.\sed0.c : \
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\libsed.h"\
+ ".\regexp.h"\
+ ".\sed.h"\
+
+
+.\sed1.c : \
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\libsed.h"\
+ ".\regexp.h"\
+ ".\sed.h"\
+
diff --git a/modules/filters/mod_sed.dsp b/modules/filters/mod_sed.dsp
new file mode 100644
index 0000000..5b1d642
--- /dev/null
+++ b/modules/filters/mod_sed.dsp
@@ -0,0 +1,135 @@
+# Microsoft Developer Studio Project File - Name="mod_sed" - 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_sed - Win32 Debug
+!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_sed.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_sed.mak" CFG="mod_sed - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_sed - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_sed - 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_sed - 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 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_sed_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"Release/mod_sed.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_sed.so" /d LONG_NAME="sed_module for Apache"
+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_sed.so" /base:@..\..\os\win32\BaseAddr.ref,mod_sed.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_sed.so" /base:@..\..\os\win32\BaseAddr.ref,mod_sed.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_sed.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "mod_sed - 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_sed_src" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"Debug/mod_sed.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_sed.so" /d LONG_NAME="sed_module for Apache"
+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_sed.so" /base:@..\..\os\win32\BaseAddr.ref,mod_sed.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_sed.so" /base:@..\..\os\win32\BaseAddr.ref,mod_sed.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_sed.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_sed - Win32 Release"
+# Name "mod_sed - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\libsed.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\mod_sed.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\regexp.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\regexp.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\sed.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\sed0.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\sed1.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
diff --git a/modules/filters/mod_sed.mak b/modules/filters/mod_sed.mak
new file mode 100644
index 0000000..c997b23
--- /dev/null
+++ b/modules/filters/mod_sed.mak
@@ -0,0 +1,380 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on mod_sed.dsp
+!IF "$(CFG)" == ""
+CFG=mod_sed - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to mod_sed - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "mod_sed - Win32 Release" && "$(CFG)" != "mod_sed - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!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_sed.mak" CFG="mod_sed - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_sed - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_sed - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(CFG)" == "mod_sed - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_sed.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_sed.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_sed.obj"
+ -@erase "$(INTDIR)\mod_sed.res"
+ -@erase "$(INTDIR)\mod_sed_src.idb"
+ -@erase "$(INTDIR)\mod_sed_src.pdb"
+ -@erase "$(INTDIR)\regexp.obj"
+ -@erase "$(INTDIR)\sed0.obj"
+ -@erase "$(INTDIR)\sed1.obj"
+ -@erase "$(OUTDIR)\mod_sed.exp"
+ -@erase "$(OUTDIR)\mod_sed.lib"
+ -@erase "$(OUTDIR)\mod_sed.pdb"
+ -@erase "$(OUTDIR)\mod_sed.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_sed_src" /FD /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_sed.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_sed.so" /d LONG_NAME="sed_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_sed.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_sed.pdb" /debug /out:"$(OUTDIR)\mod_sed.so" /implib:"$(OUTDIR)\mod_sed.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_sed.so /opt:ref
+LINK32_OBJS= \
+ "$(INTDIR)\mod_sed.obj" \
+ "$(INTDIR)\regexp.obj" \
+ "$(INTDIR)\sed0.obj" \
+ "$(INTDIR)\sed1.obj" \
+ "$(INTDIR)\mod_sed.res" \
+ "..\..\srclib\apr\Release\libapr-1.lib" \
+ "..\..\srclib\apr-util\Release\libaprutil-1.lib" \
+ "..\..\Release\libhttpd.lib"
+
+"$(OUTDIR)\mod_sed.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Release\mod_sed.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_sed.so"
+ if exist .\Release\mod_sed.so.manifest mt.exe -manifest .\Release\mod_sed.so.manifest -outputresource:.\Release\mod_sed.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ELSEIF "$(CFG)" == "mod_sed - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_sed.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_sed.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_sed.obj"
+ -@erase "$(INTDIR)\mod_sed.res"
+ -@erase "$(INTDIR)\mod_sed_src.idb"
+ -@erase "$(INTDIR)\mod_sed_src.pdb"
+ -@erase "$(INTDIR)\regexp.obj"
+ -@erase "$(INTDIR)\sed0.obj"
+ -@erase "$(INTDIR)\sed1.obj"
+ -@erase "$(OUTDIR)\mod_sed.exp"
+ -@erase "$(OUTDIR)\mod_sed.lib"
+ -@erase "$(OUTDIR)\mod_sed.pdb"
+ -@erase "$(OUTDIR)\mod_sed.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_sed_src" /FD /EHsc /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_sed.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_sed.so" /d LONG_NAME="sed_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_sed.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_sed.pdb" /debug /out:"$(OUTDIR)\mod_sed.so" /implib:"$(OUTDIR)\mod_sed.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_sed.so
+LINK32_OBJS= \
+ "$(INTDIR)\mod_sed.obj" \
+ "$(INTDIR)\regexp.obj" \
+ "$(INTDIR)\sed0.obj" \
+ "$(INTDIR)\sed1.obj" \
+ "$(INTDIR)\mod_sed.res" \
+ "..\..\srclib\apr\Debug\libapr-1.lib" \
+ "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \
+ "..\..\Debug\libhttpd.lib"
+
+"$(OUTDIR)\mod_sed.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Debug\mod_sed.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_sed.so"
+ if exist .\Debug\mod_sed.so.manifest mt.exe -manifest .\Debug\mod_sed.so.manifest -outputresource:.\Debug\mod_sed.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+
+!IF "$(NO_EXTERNAL_DEPS)" != "1"
+!IF EXISTS("mod_sed.dep")
+!INCLUDE "mod_sed.dep"
+!ELSE
+!MESSAGE Warning: cannot find "mod_sed.dep"
+!ENDIF
+!ENDIF
+
+
+!IF "$(CFG)" == "mod_sed - Win32 Release" || "$(CFG)" == "mod_sed - Win32 Debug"
+
+!IF "$(CFG)" == "mod_sed - Win32 Release"
+
+"libapr - Win32 Release" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_sed - Win32 Debug"
+
+"libapr - Win32 Debug" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_sed - Win32 Release"
+
+"libaprutil - Win32 Release" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_sed - Win32 Debug"
+
+"libaprutil - Win32 Debug" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_sed - Win32 Release"
+
+"libhttpd - Win32 Release" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 ReleaseCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_sed - Win32 Debug"
+
+"libhttpd - Win32 Debug" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 DebugCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ENDIF
+
+SOURCE=..\..\build\win32\httpd.rc
+
+!IF "$(CFG)" == "mod_sed - Win32 Release"
+
+
+"$(INTDIR)\mod_sed.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_sed.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_sed.so" /d LONG_NAME="sed_module for Apache" $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "mod_sed - Win32 Debug"
+
+
+"$(INTDIR)\mod_sed.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_sed.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_sed.so" /d LONG_NAME="sed_module for Apache" $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\mod_sed.c
+
+"$(INTDIR)\mod_sed.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\regexp.c
+
+"$(INTDIR)\regexp.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\sed0.c
+
+"$(INTDIR)\sed0.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\sed1.c
+
+"$(INTDIR)\sed1.obj" : $(SOURCE) "$(INTDIR)"
+
+
+
+!ENDIF
+
diff --git a/modules/filters/mod_substitute.c b/modules/filters/mod_substitute.c
new file mode 100644
index 0000000..d454bf3
--- /dev/null
+++ b/modules/filters/mod_substitute.c
@@ -0,0 +1,766 @@
+/* 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_substitute.c: Perform content rewriting on the fly
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "apr_general.h"
+#include "apr_strings.h"
+#include "apr_strmatch.h"
+#include "apr_lib.h"
+#include "util_filter.h"
+#include "util_varbuf.h"
+#include "apr_buckets.h"
+#include "http_request.h"
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+
+/*
+ * We want to limit the memory usage in a way that is predictable.
+ * Therefore we limit the resulting length of the line.
+ * This is the default value.
+ */
+#define AP_SUBST_MAX_LINE_LENGTH (1024*1024)
+
+static const char substitute_filter_name[] = "SUBSTITUTE";
+
+module AP_MODULE_DECLARE_DATA substitute_module;
+
+typedef struct subst_pattern_t {
+ const apr_strmatch_pattern *pattern;
+ const ap_regex_t *regexp;
+ const char *replacement;
+ apr_size_t replen;
+ apr_size_t patlen;
+ int flatten;
+ const char *from;
+} subst_pattern_t;
+
+typedef struct {
+ apr_array_header_t *patterns;
+ apr_size_t max_line_length;
+ int max_line_length_set;
+ int inherit_before;
+} subst_dir_conf;
+
+typedef struct {
+ apr_bucket_brigade *linebb;
+ apr_bucket_brigade *linesbb;
+ apr_bucket_brigade *passbb;
+ apr_bucket_brigade *pattbb;
+ apr_pool_t *tpool;
+} substitute_module_ctx;
+
+static void *create_substitute_dcfg(apr_pool_t *p, char *d)
+{
+ subst_dir_conf *dcfg =
+ (subst_dir_conf *) apr_palloc(p, sizeof(subst_dir_conf));
+
+ dcfg->patterns = apr_array_make(p, 10, sizeof(subst_pattern_t));
+ dcfg->max_line_length = AP_SUBST_MAX_LINE_LENGTH;
+ dcfg->max_line_length_set = 0;
+ dcfg->inherit_before = -1;
+ return dcfg;
+}
+
+static void *merge_substitute_dcfg(apr_pool_t *p, void *basev, void *overv)
+{
+ subst_dir_conf *a =
+ (subst_dir_conf *) apr_palloc(p, sizeof(subst_dir_conf));
+ subst_dir_conf *base = (subst_dir_conf *) basev;
+ subst_dir_conf *over = (subst_dir_conf *) overv;
+
+ a->inherit_before = (over->inherit_before != -1)
+ ? over->inherit_before
+ : base->inherit_before;
+ /* SubstituteInheritBefore wasn't the default behavior until 2.5.x,
+ * and may be re-disabled as desired; the original default behavior
+ * was to apply inherited subst patterns after locally scoped patterns.
+ * In later 2.2 and 2.4 versions, SubstituteInheritBefore may be toggled
+ * 'on' to follow the corrected/expected behavior, without violating POLS.
+ */
+ if (a->inherit_before == 1) {
+ a->patterns = apr_array_append(p, base->patterns,
+ over->patterns);
+ }
+ else {
+ a->patterns = apr_array_append(p, over->patterns,
+ base->patterns);
+ }
+ a->max_line_length = over->max_line_length_set ?
+ over->max_line_length : base->max_line_length;
+ a->max_line_length_set = over->max_line_length_set
+ | base->max_line_length_set;
+ return a;
+}
+
+#define AP_MAX_BUCKETS 1000
+
+#define SEDRMPATBCKT(b, offset, tmp_b, patlen) do { \
+ apr_bucket_split(b, offset); \
+ tmp_b = APR_BUCKET_NEXT(b); \
+ apr_bucket_split(tmp_b, patlen); \
+ b = APR_BUCKET_NEXT(tmp_b); \
+ apr_bucket_delete(tmp_b); \
+} while (0)
+
+#define CAP2LINEMAX(n) ((n) < (apr_size_t)200 ? (int)(n) : 200)
+
+static apr_status_t do_pattmatch(ap_filter_t *f, apr_bucket *inb,
+ apr_bucket_brigade *mybb,
+ apr_pool_t *pool)
+{
+ int i;
+ int force_quick = 0;
+ ap_regmatch_t regm[AP_MAX_REG_MATCH];
+ apr_size_t bytes;
+ apr_size_t len;
+ const char *buff;
+ struct ap_varbuf vb;
+ apr_bucket *b;
+ apr_bucket *tmp_b;
+
+ subst_dir_conf *cfg =
+ (subst_dir_conf *) ap_get_module_config(f->r->per_dir_config,
+ &substitute_module);
+ subst_pattern_t *script;
+
+ APR_BRIGADE_INSERT_TAIL(mybb, inb);
+ ap_varbuf_init(pool, &vb, 0);
+
+ script = (subst_pattern_t *) cfg->patterns->elts;
+ /*
+ * Simple optimization. If we only have one pattern, then
+ * we can safely avoid the overhead of flattening
+ */
+ if (cfg->patterns->nelts == 1) {
+ force_quick = 1;
+ }
+ for (i = 0; i < cfg->patterns->nelts; i++) {
+ for (b = APR_BRIGADE_FIRST(mybb);
+ b != APR_BRIGADE_SENTINEL(mybb);
+ b = APR_BUCKET_NEXT(b)) {
+ if (APR_BUCKET_IS_METADATA(b)) {
+ /*
+ * we should NEVER see this, because we should never
+ * be passed any, but "handle" it just in case.
+ */
+ continue;
+ }
+ if (apr_bucket_read(b, &buff, &bytes, APR_BLOCK_READ)
+ == APR_SUCCESS) {
+ int have_match = 0;
+
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, f->r,
+ "Line read (%" APR_SIZE_T_FMT " bytes): %.*s",
+ bytes, CAP2LINEMAX(bytes), buff);
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, f->r,
+ "Replacing %s:'%s' by '%s'",
+ script->pattern ? "string" :
+ script->regexp ? "regex" :
+ "unknown",
+ script->from, script->replacement);
+
+ vb.strlen = 0;
+ if (script->pattern) {
+ const char *repl;
+ /*
+ * space_left counts how many bytes we have left until the
+ * line length reaches max_line_length.
+ */
+ apr_size_t space_left = cfg->max_line_length;
+ apr_size_t repl_len = strlen(script->replacement);
+ while ((repl = apr_strmatch(script->pattern, buff, bytes)))
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, f->r,
+ "Matching found, result: '%s'",
+ script->replacement);
+ have_match = 1;
+ /* get offset into buff for pattern */
+ len = (apr_size_t) (repl - buff);
+ if (script->flatten && !force_quick) {
+ /*
+ * We are flattening the buckets here, meaning
+ * that we don't do the fast bucket splits.
+ * Instead we copy over what the buckets would
+ * contain and use them. This is slow, since we
+ * are constanting allocing space and copying
+ * strings.
+ */
+ if (vb.strlen + len + repl_len > cfg->max_line_length)
+ return APR_ENOMEM;
+ ap_varbuf_strmemcat(&vb, buff, len);
+ ap_varbuf_strmemcat(&vb, script->replacement, repl_len);
+ }
+ else {
+ /*
+ * The string before the match but after the
+ * previous match (if any) has length 'len'.
+ * Check if we still have space for this string and
+ * the replacement string.
+ */
+ if (space_left < len + repl_len)
+ return APR_ENOMEM;
+ space_left -= len + repl_len;
+ /*
+ * We now split off the string before the match
+ * as its own bucket, then isolate the matched
+ * string and delete it.
+ */
+ SEDRMPATBCKT(b, len, tmp_b, script->patlen);
+ /*
+ * Finally, we create a bucket that contains the
+ * replacement...
+ */
+ tmp_b = apr_bucket_transient_create(script->replacement,
+ script->replen,
+ f->r->connection->bucket_alloc);
+ /* ... and insert it */
+ APR_BUCKET_INSERT_BEFORE(b, tmp_b);
+ }
+ /* now we need to adjust buff for all these changes */
+ len += script->patlen;
+ bytes -= len;
+ buff += len;
+ }
+ if (have_match) {
+ if (script->flatten && !force_quick) {
+ /* XXX: we should check for AP_MAX_BUCKETS here and
+ * XXX: call ap_pass_brigade accordingly
+ */
+ char *copy = ap_varbuf_pdup(pool, &vb, NULL, 0,
+ buff, bytes, &len);
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, f->r,
+ "New line (%" APR_SIZE_T_FMT " bytes): %.*s",
+ len, CAP2LINEMAX(len), copy);
+ tmp_b = apr_bucket_pool_create(copy, len, pool,
+ f->r->connection->bucket_alloc);
+ APR_BUCKET_INSERT_BEFORE(b, tmp_b);
+ apr_bucket_delete(b);
+ b = tmp_b;
+ }
+ else {
+ /*
+ * We want the behaviour to be predictable.
+ * Therefore we try to always error out if the
+ * line length is larger than the limit,
+ * regardless of the content of the line. So,
+ * let's check if the remaining non-matching
+ * string does not exceed the limit.
+ */
+ if (space_left < b->length)
+ return APR_ENOMEM;
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, f->r,
+ "New line (%" APR_SIZE_T_FMT " bytes): %.*s",
+ bytes, CAP2LINEMAX(bytes), buff);
+ }
+ }
+ }
+ else if (script->regexp) {
+ int left = bytes;
+ const char *pos = buff;
+ char *repl;
+ apr_size_t space_left = cfg->max_line_length;
+ while (!ap_regexec_len(script->regexp, pos, left,
+ AP_MAX_REG_MATCH, regm, 0)) {
+ apr_status_t rv;
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, f->r,
+ "Matching found");
+ have_match = 1;
+ if (script->flatten && !force_quick) {
+ /* check remaining buffer size */
+ /* Note that the last param in ap_varbuf_regsub below
+ * must stay positive. If it gets 0, it would mean
+ * unlimited space available. */
+ if (vb.strlen + regm[0].rm_so >= cfg->max_line_length)
+ return APR_ENOMEM;
+ /* copy bytes before the match */
+ if (regm[0].rm_so > 0)
+ ap_varbuf_strmemcat(&vb, pos, regm[0].rm_so);
+ /* add replacement string, last argument is unsigned! */
+ rv = ap_varbuf_regsub(&vb, script->replacement, pos,
+ AP_MAX_REG_MATCH, regm,
+ cfg->max_line_length - vb.strlen);
+ if (rv != APR_SUCCESS)
+ return rv;
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, f->r,
+ "Result: '%s'", vb.buf);
+ }
+ else {
+ apr_size_t repl_len;
+ /* account for string before the match */
+ if (space_left <= regm[0].rm_so)
+ return APR_ENOMEM;
+ space_left -= regm[0].rm_so;
+ rv = ap_pregsub_ex(pool, &repl,
+ script->replacement, pos,
+ AP_MAX_REG_MATCH, regm,
+ space_left);
+ if (rv != APR_SUCCESS)
+ return rv;
+ repl_len = strlen(repl);
+ space_left -= repl_len;
+ len = (apr_size_t) (regm[0].rm_eo - regm[0].rm_so);
+ SEDRMPATBCKT(b, regm[0].rm_so, tmp_b, len);
+ tmp_b = apr_bucket_transient_create(repl, repl_len,
+ f->r->connection->bucket_alloc);
+ APR_BUCKET_INSERT_BEFORE(b, tmp_b);
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, f->r,
+ "Result: '%s'", repl);
+ }
+ /*
+ * reset to past what we just did. pos now maps to b
+ * again
+ */
+ pos += regm[0].rm_eo;
+ left -= regm[0].rm_eo;
+ }
+ if (have_match && script->flatten && !force_quick) {
+ char *copy;
+ /* Copy result plus the part after the last match into
+ * a bucket.
+ */
+ copy = ap_varbuf_pdup(pool, &vb, NULL, 0, pos, left,
+ &len);
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, f->r,
+ "New line (%" APR_SIZE_T_FMT " bytes): %.*s",
+ len, CAP2LINEMAX(len), copy);
+ tmp_b = apr_bucket_pool_create(copy, len, pool,
+ f->r->connection->bucket_alloc);
+ APR_BUCKET_INSERT_BEFORE(b, tmp_b);
+ apr_bucket_delete(b);
+ b = tmp_b;
+ }
+ }
+ else {
+ ap_assert(0);
+ continue;
+ }
+ }
+ }
+ script++;
+ }
+ ap_varbuf_free(&vb);
+ return APR_SUCCESS;
+}
+
+static apr_status_t substitute_filter(ap_filter_t *f, apr_bucket_brigade *bb)
+{
+ apr_size_t bytes;
+ apr_size_t len;
+ apr_size_t fbytes;
+ const char *buff;
+ const char *nl = NULL;
+ char *bflat;
+ apr_bucket *b;
+ apr_bucket *tmp_b;
+ apr_bucket_brigade *tmp_bb = NULL;
+ apr_status_t rv;
+ subst_dir_conf *cfg =
+ (subst_dir_conf *) ap_get_module_config(f->r->per_dir_config,
+ &substitute_module);
+
+ substitute_module_ctx *ctx = f->ctx;
+
+ /*
+ * First time around? Create the saved bb that we used for each pass
+ * through. Note that we can also get here when we explicitly clear ctx,
+ * for error handling
+ */
+ if (!ctx) {
+ f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
+ /*
+ * Create all the temporary brigades we need and reuse them to avoid
+ * creating them over and over again from r->pool which would cost a
+ * lot of memory in some cases.
+ */
+ ctx->linebb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+ ctx->linesbb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+ ctx->pattbb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+ /*
+ * Everything to be passed to the next filter goes in
+ * here, our pass brigade.
+ */
+ ctx->passbb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+ /* Create our temporary pool only once */
+ apr_pool_create(&(ctx->tpool), f->r->pool);
+ apr_pool_tag(ctx->tpool, "substitute_tpool");
+ apr_table_unset(f->r->headers_out, "Content-Length");
+ }
+
+ /*
+ * Shortcircuit processing
+ */
+ if (APR_BRIGADE_EMPTY(bb))
+ return APR_SUCCESS;
+
+ /*
+ * Here's the concept:
+ * Read in the data and look for newlines. Once we
+ * find a full "line", add it to our working brigade.
+ * If we've finished reading the brigade and we have
+ * any left over data (not a "full" line), store that
+ * for the next pass.
+ *
+ * Note: anything stored in ctx->linebb for sure does not have
+ * a newline char, so we don't concat that bb with the
+ * new bb, since we would spending time searching for the newline
+ * in data we know it doesn't exist. So instead, we simply scan
+ * our current bb and, if we see a newline, prepend ctx->linebb
+ * to the front of it. This makes the code much less straight-
+ * forward (otherwise we could APR_BRIGADE_CONCAT(ctx->linebb, bb)
+ * and just scan for newlines and not bother with needing to know
+ * when ctx->linebb needs to be reset) but also faster. We'll take
+ * the speed.
+ *
+ * Note: apr_brigade_split_line would be nice here, but we
+ * really can't use it since we need more control and we want
+ * to re-use already read bucket data.
+ *
+ * See mod_include if still confused :)
+ */
+
+ while ((b = APR_BRIGADE_FIRST(bb)) && (b != APR_BRIGADE_SENTINEL(bb))) {
+ if (APR_BUCKET_IS_EOS(b)) {
+ /*
+ * if we see the EOS, then we need to pass along everything we
+ * have. But if the ctx->linebb isn't empty, then we need to add
+ * that to the end of what we'll be passing.
+ */
+ if (!APR_BRIGADE_EMPTY(ctx->linebb)) {
+ rv = apr_brigade_pflatten(ctx->linebb, &bflat,
+ &fbytes, ctx->tpool);
+ if (rv != APR_SUCCESS)
+ goto err;
+ if (fbytes > cfg->max_line_length) {
+ rv = APR_ENOMEM;
+ goto err;
+ }
+ tmp_b = apr_bucket_transient_create(bflat, fbytes,
+ f->r->connection->bucket_alloc);
+ rv = do_pattmatch(f, tmp_b, ctx->pattbb, ctx->tpool);
+ if (rv != APR_SUCCESS)
+ goto err;
+ APR_BRIGADE_CONCAT(ctx->passbb, ctx->pattbb);
+ apr_brigade_cleanup(ctx->linebb);
+ }
+ APR_BUCKET_REMOVE(b);
+ APR_BRIGADE_INSERT_TAIL(ctx->passbb, b);
+ }
+ /*
+ * No need to handle FLUSH buckets separately as we call
+ * ap_pass_brigade anyway at the end of the loop.
+ */
+ else if (APR_BUCKET_IS_METADATA(b)) {
+ APR_BUCKET_REMOVE(b);
+ APR_BRIGADE_INSERT_TAIL(ctx->passbb, b);
+ }
+ else {
+ /*
+ * We have actual "data" so read in as much as we can and start
+ * scanning and splitting from our read buffer
+ */
+ rv = apr_bucket_read(b, &buff, &bytes, APR_BLOCK_READ);
+ if (rv != APR_SUCCESS || bytes == 0) {
+ apr_bucket_delete(b);
+ }
+ else {
+ int num = 0;
+ while (bytes > 0) {
+ nl = memchr(buff, '\n', bytes);
+ if (nl) {
+ len = (apr_size_t) (nl - buff) + 1;
+ /* split *after* the newline */
+ apr_bucket_split(b, len);
+ /*
+ * We've likely read more data, so bypass rereading
+ * bucket data and continue scanning through this
+ * buffer
+ */
+ bytes -= len;
+ buff += len;
+ /*
+ * we need b to be updated for future potential
+ * splitting
+ */
+ tmp_b = APR_BUCKET_NEXT(b);
+ APR_BUCKET_REMOVE(b);
+ /*
+ * Hey, we found a newline! Don't forget the old
+ * stuff that needs to be added to the front. So we
+ * add the split bucket to the end, flatten the whole
+ * bb, morph the whole shebang into a bucket which is
+ * then added to the tail of the newline bb.
+ */
+ if (!APR_BRIGADE_EMPTY(ctx->linebb)) {
+ APR_BRIGADE_INSERT_TAIL(ctx->linebb, b);
+ rv = apr_brigade_pflatten(ctx->linebb, &bflat,
+ &fbytes, ctx->tpool);
+ if (rv != APR_SUCCESS)
+ goto err;
+ if (fbytes > cfg->max_line_length) {
+ /* Avoid pflattening further lines, we will
+ * abort later on anyway.
+ */
+ rv = APR_ENOMEM;
+ goto err;
+ }
+ b = apr_bucket_transient_create(bflat, fbytes,
+ f->r->connection->bucket_alloc);
+ apr_brigade_cleanup(ctx->linebb);
+ }
+ rv = do_pattmatch(f, b, ctx->pattbb, ctx->tpool);
+ if (rv != APR_SUCCESS)
+ goto err;
+ /*
+ * Count how many buckets we have in ctx->passbb
+ * so far. Yes, this is correct we count ctx->passbb
+ * and not ctx->pattbb as we do not reset num on every
+ * iteration.
+ */
+ for (b = APR_BRIGADE_FIRST(ctx->pattbb);
+ b != APR_BRIGADE_SENTINEL(ctx->pattbb);
+ b = APR_BUCKET_NEXT(b)) {
+ num++;
+ }
+ APR_BRIGADE_CONCAT(ctx->passbb, ctx->pattbb);
+ /*
+ * If the number of buckets in ctx->passbb reaches an
+ * "insane" level, we consume much memory for all the
+ * buckets as such. So lets flush them down the chain
+ * in this case and thus clear ctx->passbb. This frees
+ * the buckets memory for further processing.
+ * Usually this condition should not become true, but
+ * it is a safety measure for edge cases.
+ */
+ if (num > AP_MAX_BUCKETS) {
+ b = apr_bucket_flush_create(
+ f->r->connection->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(ctx->passbb, b);
+ rv = ap_pass_brigade(f->next, ctx->passbb);
+ apr_brigade_cleanup(ctx->passbb);
+ num = 0;
+ apr_pool_clear(ctx->tpool);
+ if (rv != APR_SUCCESS)
+ goto err;
+ }
+ b = tmp_b;
+ }
+ else {
+ /*
+ * no newline in whatever is left of this buffer so
+ * tuck data away and get next bucket
+ */
+ APR_BUCKET_REMOVE(b);
+ APR_BRIGADE_INSERT_TAIL(ctx->linebb, b);
+ bytes = 0;
+ }
+ }
+ }
+ }
+ if (!APR_BRIGADE_EMPTY(ctx->passbb)) {
+ rv = ap_pass_brigade(f->next, ctx->passbb);
+ apr_brigade_cleanup(ctx->passbb);
+ if (rv != APR_SUCCESS)
+ goto err;
+ }
+ apr_pool_clear(ctx->tpool);
+ }
+
+ /* Anything left we want to save/setaside for the next go-around */
+ if (!APR_BRIGADE_EMPTY(ctx->linebb)) {
+ /*
+ * Provide ap_save_brigade with an existing empty brigade
+ * (ctx->linesbb) to avoid creating a new one.
+ */
+ ap_save_brigade(f, &(ctx->linesbb), &(ctx->linebb), f->r->pool);
+ tmp_bb = ctx->linebb;
+ ctx->linebb = ctx->linesbb;
+ ctx->linesbb = tmp_bb;
+ }
+
+ return APR_SUCCESS;
+err:
+ if (rv == APR_ENOMEM)
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01328) "Line too long, URI %s",
+ f->r->uri);
+ apr_pool_clear(ctx->tpool);
+ return rv;
+}
+
+static const char *set_pattern(cmd_parms *cmd, void *cfg, const char *line)
+{
+ char *from = NULL;
+ char *to = NULL;
+ char *flags = NULL;
+ char *ourline;
+ char delim;
+ subst_pattern_t *nscript;
+ int is_pattern = 0;
+ int ignore_case = 0;
+ int flatten = 1;
+ ap_regex_t *r = NULL;
+
+ if (apr_tolower(*line) != 's') {
+ return "Bad Substitute format, must be an s/// pattern";
+ }
+ ourline = apr_pstrdup(cmd->pool, line);
+ delim = *++ourline;
+ if (delim)
+ from = ++ourline;
+ if (from) {
+ if (*ourline != delim) {
+ while (*++ourline && *ourline != delim);
+ }
+ if (*ourline) {
+ *ourline = '\0';
+ to = ++ourline;
+ }
+ }
+ if (to) {
+ if (*ourline != delim) {
+ while (*++ourline && *ourline != delim);
+ }
+ if (*ourline) {
+ *ourline = '\0';
+ flags = ++ourline;
+ }
+ }
+
+ if (!delim || !from || !*from || !to) {
+ return "Bad Substitute format, must be a complete s/// pattern";
+ }
+
+ if (flags) {
+ while (*flags) {
+ delim = apr_tolower(*flags); /* re-use */
+ if (delim == 'i')
+ ignore_case = 1;
+ else if (delim == 'n')
+ is_pattern = 1;
+ else if (delim == 'f')
+ flatten = 1;
+ else if (delim == 'q')
+ flatten = 0;
+ else
+ return "Bad Substitute flag, only s///[infq] are supported";
+ flags++;
+ }
+ }
+
+ /* first see if we can compile the regex */
+ if (!is_pattern) {
+ int flags = AP_REG_NO_DEFAULT
+ | (ap_regcomp_get_default_cflags() & AP_REG_DOLLAR_ENDONLY)
+ | (ignore_case ? AP_REG_ICASE : 0);
+ r = ap_pregcomp(cmd->pool, from, flags);
+ if (!r)
+ return "Substitute could not compile regex";
+ }
+ nscript = apr_array_push(((subst_dir_conf *) cfg)->patterns);
+ /* init the new entries */
+ nscript->pattern = NULL;
+ nscript->regexp = NULL;
+ nscript->replacement = NULL;
+ nscript->patlen = 0;
+ nscript->from = from;
+
+ if (is_pattern) {
+ nscript->patlen = strlen(from);
+ nscript->pattern = apr_strmatch_precompile(cmd->pool, from,
+ !ignore_case);
+ }
+ else {
+ nscript->regexp = r;
+ }
+
+ nscript->replacement = to;
+ nscript->replen = strlen(to);
+ nscript->flatten = flatten;
+
+ return NULL;
+}
+
+#define KBYTE 1024
+#define MBYTE 1048576
+#define GBYTE 1073741824
+
+static const char *set_max_line_length(cmd_parms *cmd, void *cfg, const char *arg)
+{
+ subst_dir_conf *dcfg = (subst_dir_conf *)cfg;
+ apr_off_t max;
+ char *end;
+ apr_status_t rv;
+
+ rv = apr_strtoff(&max, arg, &end, 10);
+ if (rv == APR_SUCCESS) {
+ if ((*end == 'K' || *end == 'k') && !end[1]) {
+ max *= KBYTE;
+ }
+ else if ((*end == 'M' || *end == 'm') && !end[1]) {
+ max *= MBYTE;
+ }
+ else if ((*end == 'G' || *end == 'g') && !end[1]) {
+ max *= GBYTE;
+ }
+ else if (*end && /* neither empty nor [Bb] */
+ ((*end != 'B' && *end != 'b') || end[1])) {
+ rv = APR_EGENERAL;
+ }
+ }
+
+ if (rv != APR_SUCCESS || max < 0)
+ {
+ return "SubstituteMaxLineLength must be a non-negative integer optionally "
+ "suffixed with 'b', 'k', 'm' or 'g'.";
+ }
+ dcfg->max_line_length = (apr_size_t)max;
+ dcfg->max_line_length_set = 1;
+ return NULL;
+}
+
+#define PROTO_FLAGS AP_FILTER_PROTO_CHANGE|AP_FILTER_PROTO_CHANGE_LENGTH
+static void register_hooks(apr_pool_t *pool)
+{
+ ap_register_output_filter(substitute_filter_name, substitute_filter,
+ NULL, AP_FTYPE_RESOURCE);
+}
+
+static const command_rec substitute_cmds[] = {
+ AP_INIT_TAKE1("Substitute", set_pattern, NULL, OR_FILEINFO,
+ "Pattern to filter the response content (s/foo/bar/[inf])"),
+ AP_INIT_TAKE1("SubstituteMaxLineLength", set_max_line_length, NULL, OR_FILEINFO,
+ "Maximum line length"),
+ AP_INIT_FLAG("SubstituteInheritBefore", ap_set_flag_slot,
+ (void *)APR_OFFSETOF(subst_dir_conf, inherit_before), OR_FILEINFO,
+ "Apply inherited patterns before those of the current context"),
+ {NULL}
+};
+
+AP_DECLARE_MODULE(substitute) = {
+ STANDARD20_MODULE_STUFF,
+ create_substitute_dcfg, /* dir config creater */
+ merge_substitute_dcfg, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ substitute_cmds, /* command table */
+ register_hooks /* register hooks */
+};
diff --git a/modules/filters/mod_substitute.dep b/modules/filters/mod_substitute.dep
new file mode 100644
index 0000000..b501b34
--- /dev/null
+++ b/modules/filters/mod_substitute.dep
@@ -0,0 +1,53 @@
+# Microsoft Developer Studio Generated Dependency File, included by mod_substitute.mak
+
+..\..\build\win32\httpd.rc : \
+ "..\..\include\ap_release.h"\
+
+
+.\mod_substitute.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_expr.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_core.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_request.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\include\util_varbuf.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_strmatch.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_hash.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_lib.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+
diff --git a/modules/filters/mod_substitute.dsp b/modules/filters/mod_substitute.dsp
new file mode 100644
index 0000000..b0bd34f
--- /dev/null
+++ b/modules/filters/mod_substitute.dsp
@@ -0,0 +1,111 @@
+# Microsoft Developer Studio Project File - Name="mod_substitute" - 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_substitute - Win32 Debug
+!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_substitute.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_substitute.mak" CFG="mod_substitute - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_substitute - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_substitute - 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_substitute - 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 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_substitute_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"Release/mod_substitute.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_substitute.so" /d LONG_NAME="substitute_module for Apache"
+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_substitute.so" /base:@..\..\os\win32\BaseAddr.ref,mod_substitute.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_substitute.so" /base:@..\..\os\win32\BaseAddr.ref,mod_substitute.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_substitute.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "mod_substitute - 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_substitute_src" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"Debug/mod_substitute.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_substitute.so" /d LONG_NAME="substitute_module for Apache"
+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_substitute.so" /base:@..\..\os\win32\BaseAddr.ref,mod_substitute.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_substitute.so" /base:@..\..\os\win32\BaseAddr.ref,mod_substitute.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_substitute.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_substitute - Win32 Release"
+# Name "mod_substitute - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\mod_substitute.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
diff --git a/modules/filters/mod_substitute.mak b/modules/filters/mod_substitute.mak
new file mode 100644
index 0000000..f1538e8
--- /dev/null
+++ b/modules/filters/mod_substitute.mak
@@ -0,0 +1,353 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on mod_substitute.dsp
+!IF "$(CFG)" == ""
+CFG=mod_substitute - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to mod_substitute - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "mod_substitute - Win32 Release" && "$(CFG)" != "mod_substitute - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!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_substitute.mak" CFG="mod_substitute - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_substitute - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_substitute - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(CFG)" == "mod_substitute - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_substitute.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_substitute.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_substitute.obj"
+ -@erase "$(INTDIR)\mod_substitute.res"
+ -@erase "$(INTDIR)\mod_substitute_src.idb"
+ -@erase "$(INTDIR)\mod_substitute_src.pdb"
+ -@erase "$(OUTDIR)\mod_substitute.exp"
+ -@erase "$(OUTDIR)\mod_substitute.lib"
+ -@erase "$(OUTDIR)\mod_substitute.pdb"
+ -@erase "$(OUTDIR)\mod_substitute.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_substitute_src" /FD /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_substitute.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_substitute.so" /d LONG_NAME="substitute_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_substitute.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_substitute.pdb" /debug /out:"$(OUTDIR)\mod_substitute.so" /implib:"$(OUTDIR)\mod_substitute.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_substitute.so /opt:ref
+LINK32_OBJS= \
+ "$(INTDIR)\mod_substitute.obj" \
+ "$(INTDIR)\mod_substitute.res" \
+ "..\..\srclib\apr\Release\libapr-1.lib" \
+ "..\..\srclib\apr-util\Release\libaprutil-1.lib" \
+ "..\..\Release\libhttpd.lib"
+
+"$(OUTDIR)\mod_substitute.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Release\mod_substitute.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_substitute.so"
+ if exist .\Release\mod_substitute.so.manifest mt.exe -manifest .\Release\mod_substitute.so.manifest -outputresource:.\Release\mod_substitute.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ELSEIF "$(CFG)" == "mod_substitute - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_substitute.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_substitute.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\mod_substitute.obj"
+ -@erase "$(INTDIR)\mod_substitute.res"
+ -@erase "$(INTDIR)\mod_substitute_src.idb"
+ -@erase "$(INTDIR)\mod_substitute_src.pdb"
+ -@erase "$(OUTDIR)\mod_substitute.exp"
+ -@erase "$(OUTDIR)\mod_substitute.lib"
+ -@erase "$(OUTDIR)\mod_substitute.pdb"
+ -@erase "$(OUTDIR)\mod_substitute.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_substitute_src" /FD /EHsc /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_substitute.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_substitute.so" /d LONG_NAME="substitute_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_substitute.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_substitute.pdb" /debug /out:"$(OUTDIR)\mod_substitute.so" /implib:"$(OUTDIR)\mod_substitute.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_substitute.so
+LINK32_OBJS= \
+ "$(INTDIR)\mod_substitute.obj" \
+ "$(INTDIR)\mod_substitute.res" \
+ "..\..\srclib\apr\Debug\libapr-1.lib" \
+ "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \
+ "..\..\Debug\libhttpd.lib"
+
+"$(OUTDIR)\mod_substitute.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Debug\mod_substitute.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_substitute.so"
+ if exist .\Debug\mod_substitute.so.manifest mt.exe -manifest .\Debug\mod_substitute.so.manifest -outputresource:.\Debug\mod_substitute.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+
+!IF "$(NO_EXTERNAL_DEPS)" != "1"
+!IF EXISTS("mod_substitute.dep")
+!INCLUDE "mod_substitute.dep"
+!ELSE
+!MESSAGE Warning: cannot find "mod_substitute.dep"
+!ENDIF
+!ENDIF
+
+
+!IF "$(CFG)" == "mod_substitute - Win32 Release" || "$(CFG)" == "mod_substitute - Win32 Debug"
+
+!IF "$(CFG)" == "mod_substitute - Win32 Release"
+
+"libapr - Win32 Release" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_substitute - Win32 Debug"
+
+"libapr - Win32 Debug" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_substitute - Win32 Release"
+
+"libaprutil - Win32 Release" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_substitute - Win32 Debug"
+
+"libaprutil - Win32 Debug" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_substitute - Win32 Release"
+
+"libhttpd - Win32 Release" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 ReleaseCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_substitute - Win32 Debug"
+
+"libhttpd - Win32 Debug" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 DebugCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ENDIF
+
+SOURCE=..\..\build\win32\httpd.rc
+
+!IF "$(CFG)" == "mod_substitute - Win32 Release"
+
+
+"$(INTDIR)\mod_substitute.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_substitute.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_substitute.so" /d LONG_NAME="substitute_module for Apache" $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "mod_substitute - Win32 Debug"
+
+
+"$(INTDIR)\mod_substitute.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\mod_substitute.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_substitute.so" /d LONG_NAME="substitute_module for Apache" $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\mod_substitute.c
+
+"$(INTDIR)\mod_substitute.obj" : $(SOURCE) "$(INTDIR)"
+
+
+
+!ENDIF
+
diff --git a/modules/filters/mod_xml2enc.c b/modules/filters/mod_xml2enc.c
new file mode 100644
index 0000000..76046b4
--- /dev/null
+++ b/modules/filters/mod_xml2enc.c
@@ -0,0 +1,676 @@
+/* Copyright (c) 2007-11, WebThing Ltd
+ * Copyright (c) 2011-, The Apache Software Foundation
+ *
+ * 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.
+ */
+
+#if defined(WIN32)
+#define XML2ENC_DECLARE_EXPORT
+#endif
+
+#include <ctype.h>
+
+/* libxml2 includes unicode/[...].h files which uses C++ comments */
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic warning "-Wcomment"
+#elif defined(__GNUC__)
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-Wcomment"
+#endif
+#endif
+
+/* libxml2 */
+#include <libxml/encoding.h>
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#elif defined(__GNUC__)
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+#pragma GCC diagnostic pop
+#endif
+#endif
+
+#include "http_protocol.h"
+#include "http_config.h"
+#include "http_log.h"
+#include "apr_strings.h"
+#include "apr_xlate.h"
+
+#include "apr_optional.h"
+#include "mod_xml2enc.h"
+
+module AP_MODULE_DECLARE_DATA xml2enc_module;
+
+#define BUFLEN 8192
+#define BUF_MIN 4096
+#define APR_BRIGADE_DO(b,bb) for (b = APR_BRIGADE_FIRST(bb); \
+ b != APR_BRIGADE_SENTINEL(bb); \
+ b = APR_BUCKET_NEXT(b))
+
+#define ENC_INITIALISED 0x100
+#define ENC_SEEN_EOS 0x200
+#define ENC_SKIPTO ENCIO_SKIPTO
+
+#define HAVE_ENCODING(enc) \
+ (((enc)!=XML_CHAR_ENCODING_NONE)&&((enc)!=XML_CHAR_ENCODING_ERROR))
+
+/*
+ * XXX: Check all those ap_assert()s and replace those that should not happen
+ * XXX: with AP_DEBUG_ASSERT and those that may happen with proper error
+ * XXX: handling.
+ */
+typedef struct {
+ xmlCharEncoding xml2enc;
+ char* buf;
+ apr_size_t bytes;
+ apr_xlate_t* convset;
+ unsigned int flags;
+ apr_off_t bblen;
+ apr_bucket_brigade* bbnext;
+ apr_bucket_brigade* bbsave;
+ const char* encoding;
+} xml2ctx;
+
+typedef struct {
+ const char* default_charset;
+ xmlCharEncoding default_encoding;
+ apr_array_header_t* skipto;
+} xml2cfg;
+
+typedef struct {
+ const char* val;
+} tattr;
+
+static ap_regex_t* seek_meta_ctype;
+static ap_regex_t* seek_charset;
+
+static apr_status_t xml2enc_filter(request_rec* r, const char* enc,
+ unsigned int mode)
+{
+ /* set up a ready-initialised ctx to convert to enc, and insert filter */
+ apr_xlate_t* convset;
+ apr_status_t rv;
+ unsigned int flags = (mode ^ ENCIO);
+ if ((mode & ENCIO) == ENCIO_OUTPUT) {
+ rv = apr_xlate_open(&convset, enc, "UTF-8", r->pool);
+ flags |= ENC_INITIALISED;
+ }
+ else if ((mode & ENCIO) == ENCIO_INPUT) {
+ rv = apr_xlate_open(&convset, "UTF-8", enc, r->pool);
+ flags |= ENC_INITIALISED;
+ }
+ else if ((mode & ENCIO) == ENCIO_INPUT_CHECKS) {
+ convset = NULL;
+ rv = APR_SUCCESS; /* we'll initialise later by sniffing */
+ }
+ else {
+ rv = APR_EGENERAL;
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01426)
+ "xml2enc: bad mode %x", mode);
+ }
+ if (rv == APR_SUCCESS) {
+ xml2ctx* ctx = apr_pcalloc(r->pool, sizeof(xml2ctx));
+ ctx->flags = flags;
+ if (flags & ENC_INITIALISED) {
+ ctx->convset = convset;
+ ctx->bblen = BUFLEN;
+ ctx->buf = apr_palloc(r->pool, (apr_size_t)ctx->bblen);
+ }
+ ap_add_output_filter("xml2enc", ctx, r, r->connection);
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01427)
+ "xml2enc: Charset %s not supported.", enc) ;
+ }
+ return rv;
+}
+
+/* This needs to operate only when we're using htmlParser */
+/* Different modules may apply different rules here. Ho, hum. */
+static void fix_skipto(request_rec* r, xml2ctx* ctx)
+{
+ apr_status_t rv;
+ xml2cfg* cfg = ap_get_module_config(r->per_dir_config, &xml2enc_module);
+ if ((cfg->skipto != NULL) && (ctx->flags & ENC_SKIPTO)) {
+ int found = 0;
+ char* p = ap_strchr(ctx->buf, '<');
+ tattr* starts = (tattr*) cfg->skipto->elts;
+ while (!found && p && *p) {
+ int i;
+ for (i = 0; i < cfg->skipto->nelts; ++i) {
+ if (!strncasecmp(p+1, starts[i].val, strlen(starts[i].val))) {
+ /* found a starting element. Strip all that comes before. */
+ apr_bucket* b;
+ apr_bucket* bstart;
+ rv = apr_brigade_partition(ctx->bbsave, (p-ctx->buf),
+ &bstart);
+ ap_assert(rv == APR_SUCCESS);
+ while (b = APR_BRIGADE_FIRST(ctx->bbsave), b != bstart) {
+ apr_bucket_delete(b);
+ }
+ ctx->bytes -= (p-ctx->buf);
+ ctx->buf = p ;
+ found = 1;
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01428)
+ "Skipped to first <%s> element",
+ starts[i].val) ;
+ break;
+ }
+ }
+ p = ap_strchr(p+1, '<');
+ }
+ if (p == NULL) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01429)
+ "Failed to find start of recognised HTML!");
+ }
+ }
+}
+static void sniff_encoding(request_rec* r, xml2ctx* ctx)
+{
+ xml2cfg* cfg = NULL; /* initialise to shut compiler warnings up */
+ char* p ;
+ apr_bucket* cutb;
+ apr_bucket* cute;
+ apr_bucket* b;
+ ap_regmatch_t match[2] ;
+ apr_status_t rv;
+ const char* ctype = r->content_type;
+
+ if (ctype) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01430)
+ "Content-Type is %s", ctype) ;
+
+ /* If we've got it in the HTTP headers, there's nothing to do */
+ if (ctype && (p = ap_strcasestr(ctype, "charset=") , p != NULL)) {
+ p += 8 ;
+ if (ctx->encoding = apr_pstrndup(r->pool, p, strcspn(p, " ;") ),
+ ctx->encoding) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01431)
+ "Got charset %s from HTTP headers", ctx->encoding) ;
+ ctx->xml2enc = xmlParseCharEncoding(ctx->encoding);
+ }
+ }
+ }
+
+ /* to sniff, first we look for BOM */
+ if (ctx->xml2enc == XML_CHAR_ENCODING_NONE) {
+ ctx->xml2enc = xmlDetectCharEncoding((const xmlChar*)ctx->buf,
+ ctx->bytes);
+ if (HAVE_ENCODING(ctx->xml2enc)) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01432)
+ "Got charset from XML rules.") ;
+ ctx->encoding = xmlGetCharEncodingName(ctx->xml2enc);
+ }
+ }
+
+ /* If none of the above, look for a META-thingey */
+ /* also we're probably about to invalidate it, so we remove it. */
+ if (ap_regexec(seek_meta_ctype, ctx->buf, 1, match, 0) == 0 ) {
+ /* get markers on the start and end of the match */
+ rv = apr_brigade_partition(ctx->bbsave, match[0].rm_eo, &cute);
+ ap_assert(rv == APR_SUCCESS);
+ rv = apr_brigade_partition(ctx->bbsave, match[0].rm_so, &cutb);
+ ap_assert(rv == APR_SUCCESS);
+ /* now set length of useful buf for start-of-data hooks */
+ ctx->bytes = match[0].rm_so;
+ if (ctx->encoding == NULL) {
+ p = apr_pstrndup(r->pool, ctx->buf + match[0].rm_so,
+ match[0].rm_eo - match[0].rm_so) ;
+ if (ap_regexec(seek_charset, p, 2, match, 0) == 0) {
+ if (ctx->encoding = apr_pstrndup(r->pool, p+match[1].rm_so,
+ match[1].rm_eo - match[1].rm_so),
+ ctx->encoding) {
+ ctx->xml2enc = xmlParseCharEncoding(ctx->encoding);
+ if (HAVE_ENCODING(ctx->xml2enc))
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01433)
+ "Got charset %s from HTML META", ctx->encoding) ;
+ }
+ }
+ }
+
+ /* cut out the <meta> we're invalidating */
+ while (cutb != cute) {
+ b = APR_BUCKET_NEXT(cutb);
+ apr_bucket_delete(cutb);
+ cutb = b;
+ }
+ /* and leave a string */
+ ctx->buf[ctx->bytes] = 0;
+ }
+
+ /* either it's set to something we found or it's still the default */
+ /* Aaargh! libxml2 has undocumented <META-crap> support. So this fails
+ * if metafix is not active. Have to make it conditional.
+ *
+ * No, that means no-metafix breaks things. Deal immediately with
+ * this particular instance of metafix.
+ */
+ if (!HAVE_ENCODING(ctx->xml2enc)) {
+ cfg = ap_get_module_config(r->per_dir_config, &xml2enc_module);
+ if (!ctx->encoding) {
+ ctx->encoding = cfg->default_charset?cfg->default_charset:"ISO-8859-1";
+ }
+ /* Unsupported charset. Can we get (iconv) support through apr_xlate? */
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01434)
+ "Charset %s not supported by libxml2; trying apr_xlate",
+ ctx->encoding);
+ if (apr_xlate_open(&ctx->convset, "UTF-8", ctx->encoding, r->pool)
+ == APR_SUCCESS) {
+ ctx->xml2enc = XML_CHAR_ENCODING_UTF8 ;
+ } else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01435)
+ "Charset %s not supported. Consider aliasing it?",
+ ctx->encoding) ;
+ }
+ }
+
+ if (!HAVE_ENCODING(ctx->xml2enc)) {
+ /* Use configuration default as a last resort */
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01436)
+ "No usable charset information; using configuration default");
+ ctx->xml2enc = (cfg->default_encoding == XML_CHAR_ENCODING_NONE)
+ ? XML_CHAR_ENCODING_8859_1 : cfg->default_encoding ;
+ }
+ if (ctype && ctx->encoding) {
+ if (ap_regexec(seek_charset, ctype, 2, match, 0)) {
+ r->content_type = apr_pstrcat(r->pool, ctype, ";charset=utf-8",
+ NULL);
+ } else {
+ char* str = apr_palloc(r->pool, strlen(r->content_type) + 13
+ - (match[0].rm_eo - match[0].rm_so) + 1);
+ memcpy(str, r->content_type, match[1].rm_so);
+ memcpy(str + match[1].rm_so, "utf-8", 5);
+ strcpy(str + match[1].rm_so + 5, r->content_type+match[1].rm_eo);
+ r->content_type = str;
+ }
+ }
+}
+
+static apr_status_t xml2enc_filter_init(ap_filter_t* f)
+{
+ xml2ctx* ctx;
+ if (!f->ctx) {
+ xml2cfg* cfg = ap_get_module_config(f->r->per_dir_config,
+ &xml2enc_module);
+ f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(xml2ctx));
+ ctx->xml2enc = XML_CHAR_ENCODING_NONE;
+ if (cfg->skipto != NULL) {
+ ctx->flags |= ENC_SKIPTO;
+ }
+ }
+ return APR_SUCCESS;
+}
+static apr_status_t xml2enc_ffunc(ap_filter_t* f, apr_bucket_brigade* bb)
+{
+ xml2ctx* ctx = f->ctx;
+ apr_status_t rv;
+ apr_bucket* b;
+ apr_bucket* bstart;
+ apr_size_t insz = 0;
+ int pending_meta = 0;
+ char *ctype;
+ char *p;
+
+ if (!ctx || !f->r->content_type) {
+ /* log error about configuring this */
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb) ;
+ }
+
+ ctype = apr_pstrdup(f->r->pool, f->r->content_type);
+ for (p = ctype; *p; ++p)
+ if (isupper(*p))
+ *p = tolower(*p);
+
+ /* only act if starts-with "text/" or contains "xml" */
+ if (strncmp(ctype, "text/", 5) && !strstr(ctype, "xml")) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb) ;
+ }
+
+ if (ctx->bbsave == NULL) {
+ ctx->bbsave = apr_brigade_create(f->r->pool,
+ f->r->connection->bucket_alloc);
+ }
+ /* append to any data left over from last time */
+ APR_BRIGADE_CONCAT(ctx->bbsave, bb);
+
+ if (!(ctx->flags & ENC_INITIALISED)) {
+ /* some kind of initialisation required */
+ /* Turn all this off when post-processing */
+
+ /* if we don't have enough data to sniff but more's to come, wait */
+ apr_brigade_length(ctx->bbsave, 0, &ctx->bblen);
+ if ((ctx->bblen < BUF_MIN) && (ctx->bblen != -1)) {
+ APR_BRIGADE_DO(b, ctx->bbsave) {
+ if (APR_BUCKET_IS_EOS(b)) {
+ ctx->flags |= ENC_SEEN_EOS;
+ break;
+ }
+ }
+ if (!(ctx->flags & ENC_SEEN_EOS)) {
+ /* not enough data to sniff. Wait for more */
+ APR_BRIGADE_DO(b, ctx->bbsave) {
+ rv = apr_bucket_setaside(b, f->r->pool);
+ ap_assert(rv == APR_SUCCESS);
+ }
+ return APR_SUCCESS;
+ }
+ }
+ if (ctx->bblen == -1) {
+ ctx->bblen = BUFLEN-1;
+ }
+
+ /* flatten it into a NULL-terminated string */
+ ctx->buf = apr_palloc(f->r->pool, (apr_size_t)(ctx->bblen+1));
+ ctx->bytes = (apr_size_t)ctx->bblen;
+ rv = apr_brigade_flatten(ctx->bbsave, ctx->buf, &ctx->bytes);
+ ap_assert(rv == APR_SUCCESS);
+ ctx->buf[ctx->bytes] = 0;
+ sniff_encoding(f->r, ctx);
+
+ /* FIXME: hook here for rewriting start-of-data? */
+ /* nah, we only have one action here - call it inline */
+ fix_skipto(f->r, ctx);
+
+ /* we might change the Content-Length, so let's force its re-calculation */
+ apr_table_unset(f->r->headers_out, "Content-Length");
+
+ /* consume the data we just sniffed */
+ /* we need to omit any <meta> we just invalidated */
+ ctx->flags |= ENC_INITIALISED;
+ ap_set_module_config(f->r->request_config, &xml2enc_module, ctx);
+ }
+ if (ctx->bbnext == NULL) {
+ ctx->bbnext = apr_brigade_create(f->r->pool,
+ f->r->connection->bucket_alloc);
+ }
+
+ if (!ctx->convset) {
+ rv = ap_pass_brigade(f->next, ctx->bbsave);
+ apr_brigade_cleanup(ctx->bbsave);
+ ap_remove_output_filter(f);
+ return rv;
+ }
+ /* move the data back to bb */
+ APR_BRIGADE_CONCAT(bb, ctx->bbsave);
+
+ while (!APR_BRIGADE_EMPTY(bb)) {
+ b = APR_BRIGADE_FIRST(bb);
+ ctx->bytes = 0;
+ if (APR_BUCKET_IS_METADATA(b)) {
+ APR_BUCKET_REMOVE(b);
+ APR_BRIGADE_INSERT_TAIL(ctx->bbnext, b);
+ /* Besides FLUSH, aggregate meta buckets to send them at
+ * once below. This resource filter is over on EOS.
+ */
+ pending_meta = 1;
+ if (APR_BUCKET_IS_EOS(b)) {
+ ap_remove_output_filter(f);
+ APR_BRIGADE_CONCAT(ctx->bbnext, bb);
+ }
+ else if (!APR_BUCKET_IS_FLUSH(b)) {
+ continue;
+ }
+ }
+ if (pending_meta) {
+ pending_meta = 0;
+ /* passing meta bucket down the chain */
+ rv = ap_pass_brigade(f->next, ctx->bbnext);
+ apr_brigade_cleanup(ctx->bbnext);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ continue;
+ }
+ /* data bucket */
+ {
+ char* buf;
+ apr_size_t bytes = 0;
+ char fixbuf[BUFLEN];
+ apr_bucket* bdestroy = NULL;
+ if (insz > 0) { /* we have dangling data. Flatten it. */
+ buf = fixbuf;
+ bytes = BUFLEN;
+ rv = apr_brigade_flatten(bb, buf, &bytes);
+ ap_assert(rv == APR_SUCCESS);
+ if (bytes == insz) {
+ /* this is only what we've already tried to convert.
+ * The brigade is exhausted.
+ * Save remaining data for next time round
+ */
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, APLOGNO(01437)
+ "xml2enc: Setting aside %" APR_SIZE_T_FMT
+ " unconverted bytes", bytes);
+ rv = ap_fflush(f->next, ctx->bbnext);
+ APR_BRIGADE_CONCAT(ctx->bbsave, bb);
+ APR_BRIGADE_DO(b, ctx->bbsave) {
+ ap_assert(apr_bucket_setaside(b, f->r->pool)
+ == APR_SUCCESS);
+ }
+ return rv;
+ }
+ /* remove the data we've just read */
+ rv = apr_brigade_partition(bb, bytes, &bstart);
+ while (b = APR_BRIGADE_FIRST(bb), b != bstart) {
+ apr_bucket_delete(b);
+ }
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, APLOGNO(01438)
+ "xml2enc: consuming %" APR_SIZE_T_FMT
+ " bytes flattened", bytes);
+ }
+ else {
+ rv = apr_bucket_read(b, (const char**)&buf, &bytes,
+ APR_BLOCK_READ);
+ APR_BUCKET_REMOVE(b);
+ bdestroy = b; /* can't destroy until finished with the data */
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, APLOGNO(01439)
+ "xml2enc: consuming %" APR_SIZE_T_FMT
+ " bytes from bucket", bytes);
+ }
+ /* OK, we've got some input we can use in [buf,bytes] */
+ if (rv == APR_SUCCESS) {
+ apr_size_t consumed;
+ xml2enc_run_preprocess(f, &buf, &bytes);
+ consumed = insz = bytes;
+ while (insz > 0) {
+ apr_status_t rv2;
+ if (ctx->bytes == ctx->bblen) {
+ /* nothing was converted last time!
+ * break out of this loop!
+ */
+ b = apr_bucket_transient_create(buf+(bytes - insz), insz,
+ bb->bucket_alloc);
+ APR_BRIGADE_INSERT_HEAD(bb, b);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, APLOGNO(01440)
+ "xml2enc: reinserting %" APR_SIZE_T_FMT
+ " unconsumed bytes from bucket", insz);
+ break;
+ }
+ ctx->bytes = (apr_size_t)ctx->bblen;
+ rv = apr_xlate_conv_buffer(ctx->convset, buf+(bytes - insz),
+ &insz, ctx->buf, &ctx->bytes);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, f->r, APLOGNO(01441)
+ "xml2enc: converted %" APR_SIZE_T_FMT
+ "/%" APR_OFF_T_FMT " bytes", consumed - insz,
+ ctx->bblen - ctx->bytes);
+ consumed = insz;
+ rv2 = ap_fwrite(f->next, ctx->bbnext, ctx->buf,
+ (apr_size_t)ctx->bblen - ctx->bytes);
+ if (rv2 != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv2, f->r, APLOGNO(01442)
+ "ap_fwrite failed");
+ return rv2;
+ }
+ switch (rv) {
+ case APR_SUCCESS:
+ continue;
+ case APR_EINCOMPLETE:
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, APLOGNO(01443)
+ "INCOMPLETE");
+ continue; /* If outbuf too small, go round again.
+ * If it was inbuf, we'll break out when
+ * we test ctx->bytes == ctx->bblen
+ */
+ case APR_EINVAL: /* try skipping one bad byte */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01444)
+ "Skipping invalid byte(s) in input stream!");
+ --insz;
+ continue;
+ default:
+ /* Erk! What's this?
+ * Bail out, flush, and hope to eat the buf raw
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, APLOGNO(01445)
+ "Failed to convert input; trying it raw") ;
+ ctx->convset = NULL;
+ rv = ap_fflush(f->next, ctx->bbnext);
+ if (rv != APR_SUCCESS)
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, f->r, APLOGNO(01446)
+ "ap_fflush failed");
+ apr_brigade_cleanup(ctx->bbnext);
+ }
+ }
+ } else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, APLOGNO(01447)
+ "xml2enc: error reading data") ;
+ }
+ if (bdestroy)
+ apr_bucket_destroy(bdestroy);
+ if (rv != APR_SUCCESS)
+ return rv;
+ }
+ }
+ if (pending_meta) {
+ /* passing pending meta bucket down the chain before leaving */
+ rv = ap_pass_brigade(f->next, ctx->bbnext);
+ apr_brigade_cleanup(ctx->bbnext);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t xml2enc_charset(request_rec* r, xmlCharEncoding* encp,
+ const char** encoding)
+{
+ xml2ctx* ctx = ap_get_module_config(r->request_config, &xml2enc_module);
+ if (!ctx || !(ctx->flags & ENC_INITIALISED)) {
+ return APR_EAGAIN;
+ }
+ *encp = ctx->xml2enc;
+ *encoding = ctx->encoding;
+ return HAVE_ENCODING(ctx->xml2enc) ? APR_SUCCESS : APR_EGENERAL;
+}
+
+#define PROTO_FLAGS AP_FILTER_PROTO_CHANGE|AP_FILTER_PROTO_CHANGE_LENGTH
+static void xml2enc_hooks(apr_pool_t* pool)
+{
+ ap_register_output_filter_protocol("xml2enc", xml2enc_ffunc,
+ xml2enc_filter_init,
+ AP_FTYPE_RESOURCE, PROTO_FLAGS);
+ APR_REGISTER_OPTIONAL_FN(xml2enc_filter);
+ APR_REGISTER_OPTIONAL_FN(xml2enc_charset);
+ seek_meta_ctype = ap_pregcomp(pool,
+ "(<meta[^>]*http-equiv[ \t\r\n='\"]*content-type[^>]*>)",
+ AP_REG_EXTENDED|AP_REG_ICASE) ;
+ seek_charset = ap_pregcomp(pool, "charset=([A-Za-z0-9_-]+)",
+ AP_REG_EXTENDED|AP_REG_ICASE) ;
+}
+static const char* set_alias(cmd_parms* cmd, void* CFG,
+ const char* charset, const char* alias)
+{
+ const char* errmsg = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (errmsg != NULL)
+ return errmsg ;
+ else if (xmlAddEncodingAlias(charset, alias) == 0)
+ return NULL;
+ else
+ return "Error setting charset alias";
+}
+
+static const char* set_default(cmd_parms* cmd, void* CFG, const char* charset)
+{
+ xml2cfg* cfg = CFG;
+ cfg->default_charset = charset;
+ cfg->default_encoding = xmlParseCharEncoding(charset);
+ switch(cfg->default_encoding) {
+ case XML_CHAR_ENCODING_NONE:
+ return "Default charset not found";
+ case XML_CHAR_ENCODING_ERROR:
+ return "Invalid or unsupported default charset";
+ default:
+ return NULL;
+ }
+}
+static const char* set_skipto(cmd_parms* cmd, void* CFG, const char* arg)
+{
+ tattr* attr;
+ xml2cfg* cfg = CFG;
+ if (cfg->skipto == NULL)
+ cfg->skipto = apr_array_make(cmd->pool, 4, sizeof(tattr));
+ attr = apr_array_push(cfg->skipto) ;
+ attr->val = arg;
+ return NULL;
+}
+
+static const command_rec xml2enc_cmds[] = {
+ AP_INIT_TAKE1("xml2EncDefault", set_default, NULL, OR_ALL,
+ "Usage: xml2EncDefault charset"),
+ AP_INIT_ITERATE2("xml2EncAlias", set_alias, NULL, RSRC_CONF,
+ "EncodingAlias charset alias [more aliases]"),
+ AP_INIT_ITERATE("xml2StartParse", set_skipto, NULL, OR_ALL,
+ "Ignore anything in front of the first of these elements"),
+ { NULL }
+};
+static void* xml2enc_config(apr_pool_t* pool, char* x)
+{
+ xml2cfg* ret = apr_pcalloc(pool, sizeof(xml2cfg));
+ ret->default_encoding = XML_CHAR_ENCODING_NONE ;
+ return ret;
+}
+
+static void* xml2enc_merge(apr_pool_t* pool, void* BASE, void* ADD)
+{
+ xml2cfg* base = BASE;
+ xml2cfg* add = ADD;
+ xml2cfg* ret = apr_pcalloc(pool, sizeof(xml2cfg));
+ ret->default_encoding = (add->default_encoding == XML_CHAR_ENCODING_NONE)
+ ? base->default_encoding : add->default_encoding ;
+ ret->default_charset = add->default_charset
+ ? add->default_charset : base->default_charset;
+ ret->skipto = add->skipto ? add->skipto : base->skipto;
+ return ret;
+}
+
+AP_DECLARE_MODULE(xml2enc) = {
+ STANDARD20_MODULE_STUFF,
+ xml2enc_config,
+ xml2enc_merge,
+ NULL,
+ NULL,
+ xml2enc_cmds,
+ xml2enc_hooks
+};
+
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(xml2enc, XML2ENC, int, preprocess,
+ (ap_filter_t *f, char** bufp, apr_size_t* bytesp),
+ (f, bufp, bytesp), OK, DECLINED)
diff --git a/modules/filters/mod_xml2enc.dep b/modules/filters/mod_xml2enc.dep
new file mode 100644
index 0000000..4c89d07
--- /dev/null
+++ b/modules/filters/mod_xml2enc.dep
@@ -0,0 +1,54 @@
+# Microsoft Developer Studio Generated Dependency File, included by mod_xml2enc.mak
+
+..\..\build\win32\httpd.rc : \
+ "..\..\include\ap_release.h"\
+
+
+.\mod_xml2enc.c : \
+ "..\..\include\ap_config.h"\
+ "..\..\include\ap_config_layout.h"\
+ "..\..\include\ap_hooks.h"\
+ "..\..\include\ap_mmn.h"\
+ "..\..\include\ap_regex.h"\
+ "..\..\include\ap_release.h"\
+ "..\..\include\apache_noprobes.h"\
+ "..\..\include\http_config.h"\
+ "..\..\include\http_log.h"\
+ "..\..\include\http_protocol.h"\
+ "..\..\include\httpd.h"\
+ "..\..\include\os.h"\
+ "..\..\include\util_cfgtree.h"\
+ "..\..\include\util_filter.h"\
+ "..\..\srclib\apr-util\include\apr_buckets.h"\
+ "..\..\srclib\apr-util\include\apr_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_optional.h"\
+ "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
+ "..\..\srclib\apr-util\include\apr_uri.h"\
+ "..\..\srclib\apr-util\include\apr_xlate.h"\
+ "..\..\srclib\apr-util\include\apu.h"\
+ "..\..\srclib\apr\include\apr.h"\
+ "..\..\srclib\apr\include\apr_allocator.h"\
+ "..\..\srclib\apr\include\apr_dso.h"\
+ "..\..\srclib\apr\include\apr_errno.h"\
+ "..\..\srclib\apr\include\apr_file_info.h"\
+ "..\..\srclib\apr\include\apr_file_io.h"\
+ "..\..\srclib\apr\include\apr_general.h"\
+ "..\..\srclib\apr\include\apr_global_mutex.h"\
+ "..\..\srclib\apr\include\apr_inherit.h"\
+ "..\..\srclib\apr\include\apr_mmap.h"\
+ "..\..\srclib\apr\include\apr_network_io.h"\
+ "..\..\srclib\apr\include\apr_poll.h"\
+ "..\..\srclib\apr\include\apr_pools.h"\
+ "..\..\srclib\apr\include\apr_portable.h"\
+ "..\..\srclib\apr\include\apr_proc_mutex.h"\
+ "..\..\srclib\apr\include\apr_ring.h"\
+ "..\..\srclib\apr\include\apr_shm.h"\
+ "..\..\srclib\apr\include\apr_strings.h"\
+ "..\..\srclib\apr\include\apr_tables.h"\
+ "..\..\srclib\apr\include\apr_thread_mutex.h"\
+ "..\..\srclib\apr\include\apr_thread_proc.h"\
+ "..\..\srclib\apr\include\apr_time.h"\
+ "..\..\srclib\apr\include\apr_user.h"\
+ "..\..\srclib\apr\include\apr_want.h"\
+ ".\mod_xml2enc.h"\
+
diff --git a/modules/filters/mod_xml2enc.dsp b/modules/filters/mod_xml2enc.dsp
new file mode 100644
index 0000000..69e2904
--- /dev/null
+++ b/modules/filters/mod_xml2enc.dsp
@@ -0,0 +1,123 @@
+# Microsoft Developer Studio Project File - Name="mod_xml2enc" - 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_xml2enc - 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_xml2enc.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_xml2enc.mak" CFG="mod_xml2enc - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_xml2enc - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_xml2enc - 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_xml2enc - 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 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/libxml2/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_xml2enc_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" /i "../../include" /i "../../srclib/apr/include" /i "../../srclib/apr-util/include" /i "../../srclib/libxml2/include" /d BIN_NAME="mod_xml2enc.so" /d LONG_NAME="xml2enc_module for Apache"
+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_xml2enc.so" /base:@..\..\os\win32\BaseAddr.ref,mod_xml2enc.so
+# ADD LINK32 kernel32.lib libxml2.lib /nologo /subsystem:windows /dll /incremental:no /debug /libpath:"../../srclib/libxml2/win32/bin.msvc" /out:".\Release\mod_xml2enc.so" /base:@..\..\os\win32\BaseAddr.ref,mod_xml2enc.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_xml2enc.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "mod_xml2enc - 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/libxml2/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_xml2enc_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" /i "../../include" /i "../../srclib/apr/include" /i "../../srclib/apr-util/include" /i "../../srclib/libxml2/include" /d BIN_NAME="mod_xml2enc.so" /d LONG_NAME="xml2enc_module for Apache"
+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_xml2enc.so" /base:@..\..\os\win32\BaseAddr.ref,mod_xml2enc.so
+# ADD LINK32 kernel32.lib libxml2.lib /nologo /subsystem:windows /dll /incremental:no /debug /libpath:"../../srclib/libxml2/win32/bin.msvc" /out:".\Debug\mod_xml2enc.so" /base:@..\..\os\win32\BaseAddr.ref,mod_xml2enc.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_xml2enc.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_xml2enc - Win32 Release"
+# Name "mod_xml2enc - Win32 Debug"
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h"
+# Begin Source File
+
+SOURCE=.\mod_xml2enc.h
+# End Source File
+# End Group
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=.\mod_xml2enc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/modules/filters/mod_xml2enc.h b/modules/filters/mod_xml2enc.h
new file mode 100644
index 0000000..90d0e0f
--- /dev/null
+++ b/modules/filters/mod_xml2enc.h
@@ -0,0 +1,55 @@
+/* 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_XML2ENC
+#define MOD_XML2ENC
+
+#define ENCIO_INPUT 0x01
+#define ENCIO_OUTPUT 0x02
+#define ENCIO_INPUT_CHECKS 0x04
+#define ENCIO (ENCIO_INPUT|ENCIO_OUTPUT|ENCIO_INPUT_CHECKS)
+#define ENCIO_SKIPTO 0x10
+
+/* declarations to deal with WIN32 compile-flag-in-source-code crap */
+#if !defined(WIN32)
+#define XML2ENC_DECLARE(type) type
+#define XML2ENC_DECLARE_NONSTD(type) type
+#define XML2ENC_DECLARE_DATA
+#elif defined(XML2ENC_DECLARE_STATIC)
+#define XML2ENC_DECLARE(type) type __stdcall
+#define XML2ENC_DECLARE_NONSTD(type) type
+#define XML2ENC_DECLARE_DATA
+#elif defined(XML2ENC_DECLARE_EXPORT)
+#define XML2ENC_DECLARE(type) __declspec(dllexport) type __stdcall
+#define XML2ENC_DECLARE_NONSTD(type) __declspec(dllexport) type
+#define XML2ENC_DECLARE_DATA __declspec(dllexport)
+#else
+#define XML2ENC_DECLARE(type) __declspec(dllimport) type __stdcall
+#define XML2ENC_DECLARE_NONSTD(type) __declspec(dllimport) type
+#define XML2ENC_DECLARE_DATA __declspec(dllimport)
+#endif
+
+APR_DECLARE_OPTIONAL_FN(apr_status_t, xml2enc_charset,
+ (request_rec* r, xmlCharEncoding* enc,
+ const char** cenc));
+
+APR_DECLARE_OPTIONAL_FN(apr_status_t, xml2enc_filter,
+ (request_rec* r, const char* enc, unsigned int mode));
+
+APR_DECLARE_EXTERNAL_HOOK(xml2enc, XML2ENC, int, preprocess,
+ (ap_filter_t *f, char** bufp, apr_size_t* bytesp))
+
+#endif
diff --git a/modules/filters/mod_xml2enc.mak b/modules/filters/mod_xml2enc.mak
new file mode 100644
index 0000000..028c736
--- /dev/null
+++ b/modules/filters/mod_xml2enc.mak
@@ -0,0 +1,352 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on mod_xml2enc.dsp
+!IF "$(CFG)" == ""
+CFG=mod_xml2enc - Win32 Release
+!MESSAGE No configuration specified. Defaulting to mod_xml2enc - Win32 Release.
+!ENDIF
+
+!IF "$(CFG)" != "mod_xml2enc - Win32 Release" && "$(CFG)" != "mod_xml2enc - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!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_xml2enc.mak" CFG="mod_xml2enc - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_xml2enc - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_xml2enc - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(CFG)" == "mod_xml2enc - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_xml2enc.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_xml2enc.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\httpd.res"
+ -@erase "$(INTDIR)\mod_xml2enc.obj"
+ -@erase "$(INTDIR)\mod_xml2enc_src.idb"
+ -@erase "$(INTDIR)\mod_xml2enc_src.pdb"
+ -@erase "$(OUTDIR)\mod_xml2enc.exp"
+ -@erase "$(OUTDIR)\mod_xml2enc.lib"
+ -@erase "$(OUTDIR)\mod_xml2enc.pdb"
+ -@erase "$(OUTDIR)\mod_xml2enc.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/libxml2/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_xml2enc_src" /FD /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\httpd.res" /i "../../include" /i "../../srclib/apr/include" /i "../../srclib/apr-util/include" /i "../../srclib/libxml2/include" /d "NDEBUG" /d BIN_NAME="mod_xml2enc.so" /d LONG_NAME="xml2enc_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_xml2enc.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib libxml2.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_xml2enc.pdb" /debug /out:"$(OUTDIR)\mod_xml2enc.so" /implib:"$(OUTDIR)\mod_xml2enc.lib" /libpath:"../../srclib/libxml2/win32/bin.msvc" /base:@..\..\os\win32\BaseAddr.ref,mod_xml2enc.so /opt:ref
+LINK32_OBJS= \
+ "$(INTDIR)\mod_xml2enc.obj" \
+ "$(INTDIR)\httpd.res" \
+ "..\..\srclib\apr\Release\libapr-1.lib" \
+ "..\..\srclib\apr-util\Release\libaprutil-1.lib" \
+ "..\..\Release\libhttpd.lib"
+
+"$(OUTDIR)\mod_xml2enc.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Release\mod_xml2enc.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_xml2enc.so"
+ if exist .\Release\mod_xml2enc.so.manifest mt.exe -manifest .\Release\mod_xml2enc.so.manifest -outputresource:.\Release\mod_xml2enc.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ELSEIF "$(CFG)" == "mod_xml2enc - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\mod_xml2enc.so" "$(DS_POSTBUILD_DEP)"
+
+!ELSE
+
+ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_xml2enc.so" "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\httpd.res"
+ -@erase "$(INTDIR)\mod_xml2enc.obj"
+ -@erase "$(INTDIR)\mod_xml2enc_src.idb"
+ -@erase "$(INTDIR)\mod_xml2enc_src.pdb"
+ -@erase "$(OUTDIR)\mod_xml2enc.exp"
+ -@erase "$(OUTDIR)\mod_xml2enc.lib"
+ -@erase "$(OUTDIR)\mod_xml2enc.pdb"
+ -@erase "$(OUTDIR)\mod_xml2enc.so"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/libxml2/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_xml2enc_src" /FD /EHsc /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\httpd.res" /i "../../include" /i "../../srclib/apr/include" /i "../../srclib/apr-util/include" /i "../../srclib/libxml2/include" /d "_DEBUG" /d BIN_NAME="mod_xml2enc.so" /d LONG_NAME="xml2enc_module for Apache"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_xml2enc.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib libxml2.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_xml2enc.pdb" /debug /out:"$(OUTDIR)\mod_xml2enc.so" /implib:"$(OUTDIR)\mod_xml2enc.lib" /libpath:"../../srclib/libxml2/win32/bin.msvc" /base:@..\..\os\win32\BaseAddr.ref,mod_xml2enc.so
+LINK32_OBJS= \
+ "$(INTDIR)\mod_xml2enc.obj" \
+ "$(INTDIR)\httpd.res" \
+ "..\..\srclib\apr\Debug\libapr-1.lib" \
+ "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \
+ "..\..\Debug\libhttpd.lib"
+
+"$(OUTDIR)\mod_xml2enc.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+TargetPath=.\Debug\mod_xml2enc.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
+
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_xml2enc.so"
+ if exist .\Debug\mod_xml2enc.so.manifest mt.exe -manifest .\Debug\mod_xml2enc.so.manifest -outputresource:.\Debug\mod_xml2enc.so;2
+ echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
+
+!ENDIF
+
+
+!IF "$(NO_EXTERNAL_DEPS)" != "1"
+!IF EXISTS("mod_xml2enc.dep")
+!INCLUDE "mod_xml2enc.dep"
+!ELSE
+!MESSAGE Warning: cannot find "mod_xml2enc.dep"
+!ENDIF
+!ENDIF
+
+
+!IF "$(CFG)" == "mod_xml2enc - Win32 Release" || "$(CFG)" == "mod_xml2enc - Win32 Debug"
+SOURCE=..\..\build\win32\httpd.rc
+
+!IF "$(CFG)" == "mod_xml2enc - Win32 Release"
+
+
+"$(INTDIR)\httpd.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\httpd.res" /i "../../include" /i "../../srclib/apr/include" /i "../../srclib/apr-util/include" /i "../../srclib/libxml2/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_xml2enc.so" /d LONG_NAME="xml2enc_module for Apache" $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "mod_xml2enc - Win32 Debug"
+
+
+"$(INTDIR)\httpd.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) /l 0x409 /fo"$(INTDIR)\httpd.res" /i "../../include" /i "../../srclib/apr/include" /i "../../srclib/apr-util/include" /i "../../srclib/libxml2/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_xml2enc.so" /d LONG_NAME="xml2enc_module for Apache" $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\mod_xml2enc.c
+
+"$(INTDIR)\mod_xml2enc.obj" : $(SOURCE) "$(INTDIR)"
+
+
+!IF "$(CFG)" == "mod_xml2enc - Win32 Release"
+
+"libapr - Win32 Release" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_xml2enc - Win32 Debug"
+
+"libapr - Win32 Debug" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libapr - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_xml2enc - Win32 Release"
+
+"libaprutil - Win32 Release" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 ReleaseCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_xml2enc - Win32 Debug"
+
+"libaprutil - Win32 Debug" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug"
+ cd "..\..\modules\filters"
+
+"libaprutil - Win32 DebugCLEAN" :
+ cd ".\..\..\srclib\apr-util"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\modules\filters"
+
+!ENDIF
+
+!IF "$(CFG)" == "mod_xml2enc - Win32 Release"
+
+"libhttpd - Win32 Release" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 ReleaseCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ELSEIF "$(CFG)" == "mod_xml2enc - Win32 Debug"
+
+"libhttpd - Win32 Debug" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug"
+ cd ".\modules\filters"
+
+"libhttpd - Win32 DebugCLEAN" :
+ cd ".\..\.."
+ $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN
+ cd ".\modules\filters"
+
+!ENDIF
+
+
+!ENDIF
+
diff --git a/modules/filters/regexp.c b/modules/filters/regexp.c
new file mode 100644
index 0000000..4acccca
--- /dev/null
+++ b/modules/filters/regexp.c
@@ -0,0 +1,599 @@
+/*
+ * Copyright (c) 2005, 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
+ * All Rights Reserved
+ *
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ *
+ * Licensed 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.
+ */
+
+/* Code moved from regexp.h */
+
+#include "apr.h"
+#include "apr_lib.h"
+#if APR_HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#if APR_HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include "libsed.h"
+#include "regexp.h"
+#include "sed.h"
+
+#define GETC() ((unsigned char)*sp++)
+#define PEEKC() ((unsigned char)*sp)
+#define UNGETC(c) (--sp)
+#define SEDCOMPILE_ERROR(c) { \
+ regerrno = c; \
+ goto out; \
+ }
+#define ecmp(s1, s2, n) (strncmp(s1, s2, n) == 0)
+#define uletter(c) (isalpha(c) || c == '_')
+
+
+static unsigned char bittab[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
+
+static int regerr(sed_commands_t *commands, int err);
+static void comperr(sed_commands_t *commands, char *msg);
+static void getrnge(char *str, step_vars_storage *vars);
+static int _advance(char *, char *, step_vars_storage *);
+extern int sed_step(char *p1, char *p2, int circf, step_vars_storage *vars);
+
+
+static void comperr(sed_commands_t *commands, char *msg)
+{
+ command_errf(commands, msg, commands->linebuf);
+}
+
+/*
+*/
+static int regerr(sed_commands_t *commands, int err)
+{
+ switch(err) {
+ case 0:
+ /* No error */
+ break;
+ case 11:
+ comperr(commands, "Range endpoint too large: %s");
+ break;
+
+ case 16:
+ comperr(commands, "Bad number: %s");
+ break;
+
+ case 25:
+ comperr(commands, "``\\digit'' out of range: %s");
+ break;
+
+ case 36:
+ comperr(commands, "Illegal or missing delimiter: %s");
+ break;
+
+ case 41:
+ comperr(commands, "No remembered search string: %s");
+ break;
+
+ case 42:
+ comperr(commands, "\\( \\) imbalance: %s");
+ break;
+
+ case 43:
+ comperr(commands, "Too many \\(: %s");
+ break;
+
+ case 44:
+ comperr(commands, "More than 2 numbers given in \\{ \\}: %s");
+ break;
+
+ case 45:
+ comperr(commands, "} expected after \\: %s");
+ break;
+
+ case 46:
+ comperr(commands, "First number exceeds second in \\{ \\}: %s");
+ break;
+
+ case 49:
+ comperr(commands, "[ ] imbalance: %s");
+ break;
+
+ case 50:
+ comperr(commands, SEDERR_TMMES);
+ break;
+
+ default:
+ comperr(commands, "Unknown regexp error code %s\n");
+ break;
+ }
+ return (0);
+}
+
+
+char *sed_compile(sed_commands_t *commands, sed_comp_args *compargs,
+ char *ep, char *endbuf, int seof)
+{
+ int c;
+ int eof = seof;
+ char *lastep;
+ int cclcnt;
+ char bracket[NBRA], *bracketp;
+ int closed;
+ int neg;
+ int lc;
+ int i, cflg;
+ int iflag; /* used for non-ascii characters in brackets */
+ char *sp = commands->cp;
+ int regerrno = 0;
+
+ lastep = 0;
+ if ((c = GETC()) == eof || c == '\n') {
+ if (c == '\n') {
+ UNGETC(c);
+ }
+ commands->cp = sp;
+ goto out;
+ }
+ bracketp = bracket;
+ compargs->circf = closed = compargs->nbra = 0;
+ if (c == '^')
+ compargs->circf++;
+ else
+ UNGETC(c);
+ while (1) {
+ if (ep >= endbuf)
+ SEDCOMPILE_ERROR(50);
+ c = GETC();
+ if (c != '*' && ((c != '\\') || (PEEKC() != '{')))
+ lastep = ep;
+ if (c == eof) {
+ *ep++ = CCEOF;
+ if (bracketp != bracket)
+ SEDCOMPILE_ERROR(42);
+ commands->cp = sp;
+ goto out;
+ }
+ switch (c) {
+
+ case '.':
+ *ep++ = CDOT;
+ continue;
+
+ case '\n':
+ SEDCOMPILE_ERROR(36);
+ commands->cp = sp;
+ goto out;
+ case '*':
+ if (lastep == 0 || *lastep == CBRA || *lastep == CKET)
+ goto defchar;
+ *lastep |= STAR;
+ continue;
+
+ case '$':
+ if (PEEKC() != eof && PEEKC() != '\n')
+ goto defchar;
+ *ep++ = CDOL;
+ continue;
+
+ case '[':
+ if (&ep[17] >= endbuf)
+ SEDCOMPILE_ERROR(50);
+
+ *ep++ = CCL;
+ lc = 0;
+ for (i = 0; i < 16; i++)
+ ep[i] = 0;
+
+ neg = 0;
+ if ((c = GETC()) == '^') {
+ neg = 1;
+ c = GETC();
+ }
+ iflag = 1;
+ do {
+ c &= 0377;
+ if (c == '\0' || c == '\n')
+ SEDCOMPILE_ERROR(49);
+ if ((c & 0200) && iflag) {
+ iflag = 0;
+ if (&ep[32] >= endbuf)
+ SEDCOMPILE_ERROR(50);
+ ep[-1] = CXCL;
+ for (i = 16; i < 32; i++)
+ ep[i] = 0;
+ }
+ if (c == '-' && lc != 0) {
+ if ((c = GETC()) == ']') {
+ PLACE('-');
+ break;
+ }
+ if ((c & 0200) && iflag) {
+ iflag = 0;
+ if (&ep[32] >= endbuf)
+ SEDCOMPILE_ERROR(50);
+ ep[-1] = CXCL;
+ for (i = 16; i < 32; i++)
+ ep[i] = 0;
+ }
+ while (lc < c) {
+ PLACE(lc);
+ lc++;
+ }
+ }
+ lc = c;
+ PLACE(c);
+ } while ((c = GETC()) != ']');
+
+ if (iflag)
+ iflag = 16;
+ else
+ iflag = 32;
+
+ if (neg) {
+ if (iflag == 32) {
+ for (cclcnt = 0; cclcnt < iflag;
+ cclcnt++)
+ ep[cclcnt] ^= 0377;
+ ep[0] &= 0376;
+ } else {
+ ep[-1] = NCCL;
+ /* make nulls match so test fails */
+ ep[0] |= 01;
+ }
+ }
+
+ ep += iflag;
+
+ continue;
+
+ case '\\':
+ switch (c = GETC()) {
+
+ case '(':
+ if (compargs->nbra >= NBRA)
+ SEDCOMPILE_ERROR(43);
+ *bracketp++ = compargs->nbra;
+ *ep++ = CBRA;
+ *ep++ = compargs->nbra++;
+ continue;
+
+ case ')':
+ if (bracketp <= bracket)
+ SEDCOMPILE_ERROR(42);
+ *ep++ = CKET;
+ *ep++ = *--bracketp;
+ closed++;
+ continue;
+
+ case '{':
+ if (lastep == (char *) 0)
+ goto defchar;
+ *lastep |= RNGE;
+ cflg = 0;
+ nlim:
+ c = GETC();
+ i = 0;
+ do {
+ if ('0' <= c && c <= '9')
+ i = 10 * i + c - '0';
+ else
+ SEDCOMPILE_ERROR(16);
+ } while (((c = GETC()) != '\\') && (c != ','));
+ if (i >= 255)
+ SEDCOMPILE_ERROR(11);
+ *ep++ = i;
+ if (c == ',') {
+ if (cflg++)
+ SEDCOMPILE_ERROR(44);
+ if ((c = GETC()) == '\\')
+ *ep++ = (char) 255;
+ else {
+ UNGETC(c);
+ goto nlim;
+ /* get 2'nd number */
+ }
+ }
+ if (GETC() != '}')
+ SEDCOMPILE_ERROR(45);
+ if (!cflg) /* one number */
+ *ep++ = i;
+ else if ((ep[-1] & 0377) < (ep[-2] & 0377))
+ SEDCOMPILE_ERROR(46);
+ continue;
+
+ case '\n':
+ SEDCOMPILE_ERROR(36);
+
+ case 'n':
+ c = '\n';
+ goto defchar;
+
+ default:
+ if (c >= '1' && c <= '9') {
+ if ((c -= '1') >= closed)
+ SEDCOMPILE_ERROR(25);
+ *ep++ = CBACK;
+ *ep++ = c;
+ continue;
+ }
+ }
+ /* Drop through to default to use \ to turn off special chars */
+
+ defchar:
+ default:
+ lastep = ep;
+ *ep++ = CCHR;
+ *ep++ = c;
+ }
+ }
+out:
+ if (regerrno) {
+ regerr(commands, regerrno);
+ return (char*) NULL;
+ }
+ /* XXX : Basant : what extra */
+ /* int reglength = (int)(ep - expbuf); */
+ return ep;
+}
+
+int sed_step(char *p1, char *p2, int circf, step_vars_storage *vars)
+{
+ int c;
+
+
+ if (circf) {
+ vars->loc1 = p1;
+ return (_advance(p1, p2, vars));
+ }
+ /* fast check for first character */
+ if (*p2 == CCHR) {
+ c = p2[1];
+ do {
+ if (*p1 != c)
+ continue;
+ if (_advance(p1, p2, vars)) {
+ vars->loc1 = p1;
+ return (1);
+ }
+ } while (*p1++);
+ return (0);
+ }
+ /* regular algorithm */
+ do {
+ if (_advance(p1, p2, vars)) {
+ vars->loc1 = p1;
+ return (1);
+ }
+ } while (*p1++);
+ return (0);
+}
+
+static int _advance(char *lp, char *ep, step_vars_storage *vars)
+{
+ char *curlp;
+ int c;
+ char *bbeg;
+ char neg;
+ int ct;
+ int epint; /* int value of *ep */
+
+ while (1) {
+ neg = 0;
+ switch (*ep++) {
+
+ case CCHR:
+ if (*ep++ == *lp++)
+ continue;
+ return (0);
+
+ case CDOT:
+ if (*lp++)
+ continue;
+ return (0);
+
+ case CDOL:
+ if (*lp == 0)
+ continue;
+ return (0);
+
+ case CCEOF:
+ vars->loc2 = lp;
+ return (1);
+
+ case CXCL:
+ c = (unsigned char)*lp++;
+ if (ISTHERE(c)) {
+ ep += 32;
+ continue;
+ }
+ return (0);
+
+ case NCCL:
+ neg = 1;
+
+ case CCL:
+ c = *lp++;
+ if (((c & 0200) == 0 && ISTHERE(c)) ^ neg) {
+ ep += 16;
+ continue;
+ }
+ return (0);
+
+ case CBRA:
+ epint = (int) *ep;
+ vars->braslist[epint] = lp;
+ ep++;
+ continue;
+
+ case CKET:
+ epint = (int) *ep;
+ vars->braelist[epint] = lp;
+ ep++;
+ continue;
+
+ case CCHR | RNGE:
+ c = *ep++;
+ getrnge(ep, vars);
+ while (vars->low--)
+ if (*lp++ != c)
+ return (0);
+ curlp = lp;
+ while (vars->size--)
+ if (*lp++ != c)
+ break;
+ if (vars->size < 0)
+ lp++;
+ ep += 2;
+ goto star;
+
+ case CDOT | RNGE:
+ getrnge(ep, vars);
+ while (vars->low--)
+ if (*lp++ == '\0')
+ return (0);
+ curlp = lp;
+ while (vars->size--)
+ if (*lp++ == '\0')
+ break;
+ if (vars->size < 0)
+ lp++;
+ ep += 2;
+ goto star;
+
+ case CXCL | RNGE:
+ getrnge(ep + 32, vars);
+ while (vars->low--) {
+ c = (unsigned char)*lp++;
+ if (!ISTHERE(c))
+ return (0);
+ }
+ curlp = lp;
+ while (vars->size--) {
+ c = (unsigned char)*lp++;
+ if (!ISTHERE(c))
+ break;
+ }
+ if (vars->size < 0)
+ lp++;
+ ep += 34; /* 32 + 2 */
+ goto star;
+
+ case NCCL | RNGE:
+ neg = 1;
+
+ case CCL | RNGE:
+ getrnge(ep + 16, vars);
+ while (vars->low--) {
+ c = *lp++;
+ if (((c & 0200) || !ISTHERE(c)) ^ neg)
+ return (0);
+ }
+ curlp = lp;
+ while (vars->size--) {
+ c = *lp++;
+ if (((c & 0200) || !ISTHERE(c)) ^ neg)
+ break;
+ }
+ if (vars->size < 0)
+ lp++;
+ ep += 18; /* 16 + 2 */
+ goto star;
+
+ case CBACK:
+ epint = (int) *ep;
+ bbeg = vars->braslist[epint];
+ ct = vars->braelist[epint] - bbeg;
+ ep++;
+
+ if (ecmp(bbeg, lp, ct)) {
+ lp += ct;
+ continue;
+ }
+ return (0);
+
+ case CBACK | STAR:
+ epint = (int) *ep;
+ bbeg = vars->braslist[epint];
+ ct = vars->braelist[epint] - bbeg;
+ ep++;
+ curlp = lp;
+ while (ecmp(bbeg, lp, ct))
+ lp += ct;
+
+ while (lp >= curlp) {
+ if (_advance(lp, ep, vars))
+ return (1);
+ lp -= ct;
+ }
+ return (0);
+
+
+ case CDOT | STAR:
+ curlp = lp;
+ while (*lp++);
+ goto star;
+
+ case CCHR | STAR:
+ curlp = lp;
+ while (*lp++ == *ep);
+ ep++;
+ goto star;
+
+ case CXCL | STAR:
+ curlp = lp;
+ do {
+ c = (unsigned char)*lp++;
+ } while (ISTHERE(c));
+ ep += 32;
+ goto star;
+
+ case NCCL | STAR:
+ neg = 1;
+
+ case CCL | STAR:
+ curlp = lp;
+ do {
+ c = *lp++;
+ } while (((c & 0200) == 0 && ISTHERE(c)) ^ neg);
+ ep += 16;
+ goto star;
+
+ star:
+ do {
+ if (--lp == vars->locs)
+ break;
+ if (_advance(lp, ep, vars))
+ return (1);
+ } while (lp > curlp);
+ return (0);
+
+ }
+ }
+}
+
+static void getrnge(char *str, step_vars_storage *vars)
+{
+ vars->low = *str++ & 0377;
+ vars->size = ((*str & 0377) == 255)? 20000: (*str &0377) - vars->low;
+}
+
+
diff --git a/modules/filters/regexp.h b/modules/filters/regexp.h
new file mode 100644
index 0000000..dc4993a
--- /dev/null
+++ b/modules/filters/regexp.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2005, 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
+ * All Rights Reserved
+ *
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ *
+ * Licensed 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 _REGEXP_H
+#define _REGEXP_H
+
+#include "libsed.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CBRA 2
+#define CCHR 4
+#define CDOT 8
+#define CCL 12
+#define CXCL 16
+#define CDOL 20
+#define CCEOF 22
+#define CKET 24
+#define CBACK 36
+#define NCCL 40
+
+#define STAR 01
+#define RNGE 03
+
+#define NBRA 9
+
+#define PLACE(c) ep[c >> 3] |= bittab[c & 07]
+#define ISTHERE(c) (ep[c >> 3] & bittab[c & 07])
+
+typedef struct _step_vars_storage {
+ char *loc1, *loc2, *locs;
+ char *braslist[NBRA];
+ char *braelist[NBRA];
+ int low;
+ int size;
+} step_vars_storage;
+
+typedef struct _sed_comp_args {
+ int circf; /* Regular expression starts with ^ */
+ int nbra; /* braces count */
+} sed_comp_args;
+
+extern char *sed_compile(sed_commands_t *commands, sed_comp_args *compargs,
+ char *ep, char *endbuf, int seof);
+extern void command_errf(sed_commands_t *commands, const char *fmt, ...)
+ __attribute__((format(printf,2,3)));
+
+#define SEDERR_CGMES "command garbled: %s"
+#define SEDERR_SMMES "Space missing before filename: %s"
+#define SEDERR_TMMES "too much command text: %s"
+#define SEDERR_LTLMES "label too long: %s"
+#define SEDERR_ULMES "undefined label: %s"
+#define SEDERR_DLMES "duplicate labels: %s"
+#define SEDERR_TMLMES "too many labels: %s"
+#define SEDERR_AD0MES "no addresses allowed: %s"
+#define SEDERR_AD1MES "only one address allowed: %s"
+#define SEDERR_TOOBIG "suffix too large: %s"
+#define SEDERR_OOMMES "out of memory"
+#define SEDERR_COPFMES "cannot open pattern file: %s"
+#define SEDERR_COIFMES "cannot open input file: %s"
+#define SEDERR_TMOMES "too many {'s"
+#define SEDERR_TMCMES "too many }'s"
+#define SEDERR_NRMES "first RE may not be null"
+#define SEDERR_UCMES "unrecognized command: %s"
+#define SEDERR_TMWFMES "too many files in w commands"
+#define SEDERR_COMES "cannot open %s"
+#define SEDERR_CCMES "cannot create %s"
+#define SEDERR_TMLNMES "too many line numbers"
+#define SEDERR_TMAMES "too many appends after line %" APR_INT64_T_FMT
+#define SEDERR_TMRMES "too many reads after line %" APR_INT64_T_FMT
+#define SEDERR_DOORNG "``\\digit'' out of range: %s"
+#define SEDERR_EDMOSUB "ending delimiter missing on substitution: %s"
+#define SEDERR_EDMOSTR "ending delimiter missing on string: %s"
+#define SEDERR_FNTL "file name too long: %s"
+#define SEDERR_CLTL "command line too long"
+#define SEDERR_TSNTSS "transform strings not the same size: %s"
+#define SEDERR_OLTL "output line too long."
+#define SEDERR_HSOVERFLOW "hold space overflowed."
+#define SEDERR_INTERNAL "internal sed error"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _REGEXP_H */
diff --git a/modules/filters/sed.h b/modules/filters/sed.h
new file mode 100644
index 0000000..cc3c03e
--- /dev/null
+++ b/modules/filters/sed.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2005, 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1984 AT&T
+ * All Rights Reserved
+ *
+ * Licensed 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 _SED_H
+#define _SED_H
+
+#include <stdlib.h>
+#include <limits.h>
+
+#define CEND 16
+#define CLNUM 14
+
+#define RESIZE 10000
+#define LBSIZE 1000
+
+#define ACOM 01
+#define BCOM 020
+#define CCOM 02
+#define CDCOM 025
+#define CNCOM 022
+#define COCOM 017
+#define CPCOM 023
+#define DCOM 03
+#define ECOM 015
+#define EQCOM 013
+#define FCOM 016
+#define GCOM 027
+#define CGCOM 030
+#define HCOM 031
+#define CHCOM 032
+#define ICOM 04
+#define LCOM 05
+#define NCOM 012
+#define PCOM 010
+#define QCOM 011
+#define RCOM 06
+#define SCOM 07
+#define TCOM 021
+#define WCOM 014
+#define CWCOM 024
+#define YCOM 026
+#define XCOM 033
+
+#endif /* _SED_H */
diff --git a/modules/filters/sed0.c b/modules/filters/sed0.c
new file mode 100644
index 0000000..a044f64
--- /dev/null
+++ b/modules/filters/sed0.c
@@ -0,0 +1,1026 @@
+/*
+ * Copyright (c) 2005, 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1984 AT&T
+ * All Rights Reserved
+ *
+ * Licensed 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.
+ */
+
+#include "apr.h"
+#include "apr_strings.h"
+#include "libsed.h"
+#include "sed.h"
+#include "regexp.h"
+
+#define CCEOF 22
+
+static int fcomp(sed_commands_t *commands, apr_file_t *fin);
+static char *compsub(sed_commands_t *commands,
+ sed_comp_args *compargs, char *rhsbuf);
+static int rline(sed_commands_t *commands, apr_file_t *fin,
+ char *lbuf, char *lbend);
+static char *address(sed_commands_t *commands, char *expbuf,
+ apr_status_t* status);
+static char *text(sed_commands_t *commands, char *textbuf, char *endbuf);
+static sed_label_t *search(sed_commands_t *commands);
+static char *ycomp(sed_commands_t *commands, char *expbuf);
+static char *comple(sed_commands_t *commands, sed_comp_args *compargs,
+ char *x1, char *ep, char *x3, char x4);
+static sed_reptr_t *alloc_reptr(sed_commands_t *commands);
+static int check_finalized(const sed_commands_t *commands);
+
+void command_errf(sed_commands_t *commands, const char *fmt, ...)
+{
+ if (commands->errfn && commands->pool) {
+ va_list args;
+ const char* error;
+ va_start(args, fmt);
+ error = apr_pvsprintf(commands->pool, fmt, args);
+ commands->errfn(commands->data, error);
+ va_end(args);
+ }
+}
+
+/*
+ * sed_init_commands
+ */
+apr_status_t sed_init_commands(sed_commands_t *commands, sed_err_fn_t *errfn, void *data,
+ apr_pool_t *p)
+{
+ memset(commands, 0, sizeof(*commands));
+
+ commands->errfn = errfn;
+ commands->data = data;
+
+ commands->labtab = commands->ltab;
+ commands->lab = commands->labtab + 1;
+ commands->pool = p;
+
+ commands->respace = apr_pcalloc(p, RESIZE);
+ if (commands->respace == NULL) {
+ command_errf(commands, SEDERR_OOMMES);
+ return APR_EGENERAL;
+ }
+
+ commands->rep = alloc_reptr(commands);
+ if (commands->rep == NULL)
+ return APR_EGENERAL;
+
+ commands->rep->ad1 = commands->respace;
+ commands->reend = &commands->respace[RESIZE - 1];
+ commands->labend = &commands->labtab[SED_LABSIZE];
+ commands->canbefinal = 1;
+
+ return APR_SUCCESS;
+}
+
+/*
+ * sed_destroy_commands
+ */
+void sed_destroy_commands(sed_commands_t *commands)
+{
+}
+
+/*
+ * sed_compile_string
+ */
+apr_status_t sed_compile_string(sed_commands_t *commands, const char *s)
+{
+ apr_status_t rv;
+
+ commands->earg = s;
+ commands->eflag = 1;
+
+ rv = fcomp(commands, NULL);
+ if (rv == APR_SUCCESS)
+ commands->canbefinal = check_finalized(commands);
+
+ commands->eflag = 0;
+
+ return (rv != 0 ? APR_EGENERAL : APR_SUCCESS);
+}
+
+/*
+ * sed_compile_file
+ */
+apr_status_t sed_compile_file(sed_commands_t *commands, apr_file_t *fin)
+{
+ apr_status_t rv = fcomp(commands, fin);
+ return (rv != 0 ? APR_EGENERAL : APR_SUCCESS);
+}
+
+/*
+ * sed_get_finalize_error
+ */
+char* sed_get_finalize_error(const sed_commands_t *commands, apr_pool_t* pool)
+{
+ const sed_label_t *lab;
+ if (commands->depth) {
+ return SEDERR_TMOMES;
+ }
+
+ /* Empty branch chain is not a issue */
+ for (lab = commands->labtab + 1; lab < commands->lab; lab++) {
+ char *error;
+ if (lab->address == 0) {
+ error = apr_psprintf(pool, SEDERR_ULMES, lab->asc);
+ return error;
+ }
+
+ if (lab->chain) {
+ return SEDERR_INTERNAL;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * sed_canbe_finalized
+ */
+int sed_canbe_finalized(const sed_commands_t *commands)
+{
+ return commands->canbefinal;
+}
+
+/*
+ * check_finalized
+ */
+static int check_finalized(const sed_commands_t *commands)
+{
+ const sed_label_t *lab;
+ if (commands->depth) {
+ return 0;
+ }
+
+ /* Empty branch chain is not a issue */
+ for (lab = commands->labtab + 1; lab < commands->lab; lab++) {
+ if (lab->address == 0 || (lab->chain)) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * dechain
+ */
+static void dechain(sed_label_t *lpt, sed_reptr_t *address)
+{
+ sed_reptr_t *rep;
+ if ((lpt == NULL) || (lpt->chain == NULL) || (address == NULL))
+ return;
+ rep = lpt->chain;
+ while (rep->lb1) {
+ sed_reptr_t *next;
+
+ next = rep->lb1;
+ rep->lb1 = address;
+ rep = next;
+ }
+ rep->lb1 = address;
+ lpt->chain = NULL;
+}
+
+/*
+ * fcomp
+ */
+static int fcomp(sed_commands_t *commands, apr_file_t *fin)
+{
+ char *p, *op, *tp;
+ sed_reptr_t *pt, *pt1;
+ int i, ii;
+ sed_label_t *lpt;
+ char fnamebuf[APR_PATH_MAX];
+ apr_status_t status;
+ sed_comp_args compargs;
+
+ op = commands->lastre;
+ if (!commands->linebuf) {
+ commands->linebuf = apr_pcalloc(commands->pool, LBSIZE + 1);
+ }
+
+ if (rline(commands, fin, commands->linebuf,
+ (commands->linebuf + LBSIZE + 1)) < 0)
+ return 0;
+ if (*commands->linebuf == '#') {
+ if (commands->linebuf[1] == 'n')
+ commands->nflag = 1;
+ }
+ else {
+ commands->cp = commands->linebuf;
+ goto comploop;
+ }
+
+ for (;;) {
+ if (rline(commands, fin, commands->linebuf,
+ (commands->linebuf + LBSIZE + 1)) < 0)
+ break;
+
+ commands->cp = commands->linebuf;
+
+comploop:
+ while (*commands->cp == ' ' || *commands->cp == '\t')
+ commands->cp++;
+ if (*commands->cp == '\0' || *commands->cp == '#')
+ continue;
+ if (*commands->cp == ';') {
+ commands->cp++;
+ goto comploop;
+ }
+
+ p = address(commands, commands->rep->ad1, &status);
+ if (status != APR_SUCCESS) {
+ command_errf(commands, SEDERR_CGMES, commands->linebuf);
+ return -1;
+ }
+
+ if (p == commands->rep->ad1) {
+ if (op)
+ commands->rep->ad1 = op;
+ else {
+ command_errf(commands, SEDERR_NRMES);
+ return -1;
+ }
+ } else if (p == 0) {
+ p = commands->rep->ad1;
+ commands->rep->ad1 = 0;
+ } else {
+ op = commands->rep->ad1;
+ if (*commands->cp == ',' || *commands->cp == ';') {
+ commands->cp++;
+ commands->rep->ad2 = p;
+ p = address(commands, commands->rep->ad2, &status);
+ if ((status != APR_SUCCESS) || (p == 0)) {
+ command_errf(commands, SEDERR_CGMES, commands->linebuf);
+ return -1;
+ }
+ if (p == commands->rep->ad2)
+ commands->rep->ad2 = op;
+ else
+ op = commands->rep->ad2;
+ } else
+ commands->rep->ad2 = 0;
+ }
+
+ if(p > &commands->respace[RESIZE-1]) {
+ command_errf(commands, SEDERR_TMMES, commands->linebuf);
+ return -1;
+ }
+
+ while (*commands->cp == ' ' || *commands->cp == '\t')
+ commands->cp++;
+
+swit:
+ switch(*commands->cp++) {
+ default:
+ command_errf(commands, SEDERR_UCMES, commands->linebuf);
+ return -1;
+
+ case '!':
+ commands->rep->negfl = 1;
+ goto swit;
+
+ case '{':
+ commands->rep->command = BCOM;
+ commands->rep->negfl = !(commands->rep->negfl);
+ commands->cmpend[commands->depth++] = &commands->rep->lb1;
+ commands->rep = alloc_reptr(commands);
+ commands->rep->ad1 = p;
+ if (*commands->cp == '\0')
+ continue;
+ goto comploop;
+
+ case '}':
+ if (commands->rep->ad1) {
+ command_errf(commands, SEDERR_AD0MES, commands->linebuf);
+ return -1;
+ }
+
+ if (--commands->depth < 0) {
+ command_errf(commands, SEDERR_TMCMES);
+ return -1;
+ }
+ *commands->cmpend[commands->depth] = commands->rep;
+
+ commands->rep->ad1 = p;
+ continue;
+
+ case '=':
+ commands->rep->command = EQCOM;
+ if (commands->rep->ad2) {
+ command_errf(commands, SEDERR_AD1MES, commands->linebuf);
+ return -1;
+ }
+ break;
+
+ case ':':
+ if (commands->rep->ad1) {
+ command_errf(commands, SEDERR_AD0MES, commands->linebuf);
+ return -1;
+ }
+
+ while (*commands->cp++ == ' ');
+ commands->cp--;
+
+ tp = commands->lab->asc;
+ while ((*tp++ = *commands->cp++)) {
+ if (tp >= &(commands->lab->asc[8])) {
+ command_errf(commands, SEDERR_LTLMES, commands->linebuf);
+ return -1;
+ }
+ }
+ *--tp = '\0';
+
+ if ((lpt = search(commands)) != NULL) {
+ if (lpt->address) {
+ command_errf(commands, SEDERR_DLMES, commands->linebuf);
+ return -1;
+ }
+ dechain(lpt, commands->rep);
+ } else {
+ commands->lab->chain = 0;
+ lpt = commands->lab;
+ if (++commands->lab >= commands->labend) {
+ command_errf(commands, SEDERR_TMLMES, commands->linebuf);
+ return -1;
+ }
+ }
+ lpt->address = commands->rep;
+ commands->rep->ad1 = p;
+
+ continue;
+
+ case 'a':
+ commands->rep->command = ACOM;
+ if (commands->rep->ad2) {
+ command_errf(commands, SEDERR_AD1MES, commands->linebuf);
+ return -1;
+ }
+ if (*commands->cp == '\\')
+ commands->cp++;
+ if (*commands->cp++ != '\n') {
+ command_errf(commands, SEDERR_CGMES, commands->linebuf);
+ return -1;
+ }
+ commands->rep->re1 = p;
+ p = text(commands, commands->rep->re1, commands->reend);
+ if (p == NULL)
+ return -1;
+ break;
+
+ case 'c':
+ commands->rep->command = CCOM;
+ if (*commands->cp == '\\') commands->cp++;
+ if (*commands->cp++ != ('\n')) {
+ command_errf(commands, SEDERR_CGMES, commands->linebuf);
+ return -1;
+ }
+ commands->rep->re1 = p;
+ p = text(commands, commands->rep->re1, commands->reend);
+ if (p == NULL)
+ return -1;
+ break;
+
+ case 'i':
+ commands->rep->command = ICOM;
+ if (commands->rep->ad2) {
+ command_errf(commands, SEDERR_AD1MES, commands->linebuf);
+ return -1;
+ }
+ if (*commands->cp == '\\') commands->cp++;
+ if (*commands->cp++ != ('\n')) {
+ command_errf(commands, SEDERR_CGMES, commands->linebuf);
+ return -1;
+ }
+ commands->rep->re1 = p;
+ p = text(commands, commands->rep->re1, commands->reend);
+ if (p == NULL)
+ return -1;
+ break;
+
+ case 'g':
+ commands->rep->command = GCOM;
+ break;
+
+ case 'G':
+ commands->rep->command = CGCOM;
+ break;
+
+ case 'h':
+ commands->rep->command = HCOM;
+ break;
+
+ case 'H':
+ commands->rep->command = CHCOM;
+ break;
+
+ case 't':
+ commands->rep->command = TCOM;
+ goto jtcommon;
+
+ case 'b':
+ commands->rep->command = BCOM;
+jtcommon:
+ while (*commands->cp++ == ' ');
+ commands->cp--;
+
+ if (*commands->cp == '\0') {
+ if ((pt = commands->labtab->chain) != NULL) {
+ while ((pt1 = pt->lb1) != NULL)
+ pt = pt1;
+ pt->lb1 = commands->rep;
+ } else
+ commands->labtab->chain = commands->rep;
+ break;
+ }
+ tp = commands->lab->asc;
+ while ((*tp++ = *commands->cp++))
+ if (tp >= &(commands->lab->asc[8])) {
+ command_errf(commands, SEDERR_LTLMES, commands->linebuf);
+ return -1;
+ }
+ commands->cp--;
+ *--tp = '\0';
+
+ if ((lpt = search(commands)) != NULL) {
+ if (lpt->address) {
+ commands->rep->lb1 = lpt->address;
+ } else {
+ pt = lpt->chain;
+ while ((pt1 = pt->lb1) != NULL)
+ pt = pt1;
+ pt->lb1 = commands->rep;
+ }
+ } else {
+ commands->lab->chain = commands->rep;
+ commands->lab->address = 0;
+ if (++commands->lab >= commands->labend) {
+ command_errf(commands, SEDERR_TMLMES, commands->linebuf);
+ return -1;
+ }
+ }
+ break;
+
+ case 'n':
+ commands->rep->command = NCOM;
+ break;
+
+ case 'N':
+ commands->rep->command = CNCOM;
+ break;
+
+ case 'p':
+ commands->rep->command = PCOM;
+ break;
+
+ case 'P':
+ commands->rep->command = CPCOM;
+ break;
+
+ case 'r':
+ commands->rep->command = RCOM;
+ if (commands->rep->ad2) {
+ command_errf(commands, SEDERR_AD1MES, commands->linebuf);
+ return -1;
+ }
+ if (*commands->cp++ != ' ') {
+ command_errf(commands, SEDERR_CGMES, commands->linebuf);
+ return -1;
+ }
+ commands->rep->re1 = p;
+ p = text(commands, commands->rep->re1, commands->reend);
+ if (p == NULL)
+ return -1;
+ break;
+
+ case 'd':
+ commands->rep->command = DCOM;
+ break;
+
+ case 'D':
+ commands->rep->command = CDCOM;
+ commands->rep->lb1 = commands->ptrspace;
+ break;
+
+ case 'q':
+ commands->rep->command = QCOM;
+ if (commands->rep->ad2) {
+ command_errf(commands, SEDERR_AD1MES, commands->linebuf);
+ return -1;
+ }
+ break;
+
+ case 'l':
+ commands->rep->command = LCOM;
+ break;
+
+ case 's':
+ commands->rep->command = SCOM;
+ commands->sseof = *commands->cp++;
+ commands->rep->re1 = p;
+ p = comple(commands, &compargs, (char *) 0, commands->rep->re1,
+ commands->reend, commands->sseof);
+ if (p == NULL)
+ return -1;
+ if (p == commands->rep->re1) {
+ if (op)
+ commands->rep->re1 = op;
+ else {
+ command_errf(commands, SEDERR_NRMES);
+ return -1;
+ }
+ } else
+ op = commands->rep->re1;
+ commands->rep->rhs = p;
+
+ p = compsub(commands, &compargs, commands->rep->rhs);
+ if ((p) == NULL)
+ return -1;
+
+ if (*commands->cp == 'g') {
+ commands->cp++;
+ commands->rep->gfl = 999;
+ } else if (commands->gflag)
+ commands->rep->gfl = 999;
+
+ if (*commands->cp >= '1' && *commands->cp <= '9') {
+ i = *commands->cp - '0';
+ commands->cp++;
+ while (1) {
+ ii = *commands->cp;
+ if (ii < '0' || ii > '9')
+ break;
+ i = i*10 + ii - '0';
+ if (i > 512) {
+ command_errf(commands, SEDERR_TOOBIG, commands->linebuf);
+ return -1;
+ }
+ commands->cp++;
+ }
+ commands->rep->gfl = i;
+ }
+
+ if (*commands->cp == 'p') {
+ commands->cp++;
+ commands->rep->pfl = 1;
+ }
+
+ if (*commands->cp == 'P') {
+ commands->cp++;
+ commands->rep->pfl = 2;
+ }
+
+ if (*commands->cp == 'w') {
+ commands->cp++;
+ if (*commands->cp++ != ' ') {
+ command_errf(commands, SEDERR_SMMES, commands->linebuf);
+ return -1;
+ }
+ if (text(commands, fnamebuf, &fnamebuf[APR_PATH_MAX-1]) == NULL) {
+ command_errf(commands, SEDERR_FNTL, commands->linebuf);
+ return -1;
+ }
+ for (i = commands->nfiles - 1; i >= 0; i--)
+ if (strcmp(fnamebuf,commands->fname[i]) == 0) {
+ commands->rep->findex = i;
+ goto done;
+ }
+ if (commands->nfiles >= NWFILES) {
+ command_errf(commands, SEDERR_TMWFMES);
+ return -1;
+ }
+ commands->fname[commands->nfiles] =
+ apr_pstrdup(commands->pool, fnamebuf);
+ if (commands->fname[commands->nfiles] == NULL) {
+ command_errf(commands, SEDERR_OOMMES);
+ return -1;
+ }
+ commands->rep->findex = commands->nfiles++;
+ }
+ break;
+
+ case 'w':
+ commands->rep->command = WCOM;
+ if (*commands->cp++ != ' ') {
+ command_errf(commands, SEDERR_SMMES, commands->linebuf);
+ return -1;
+ }
+ if (text(commands, fnamebuf, &fnamebuf[APR_PATH_MAX-1]) == NULL) {
+ command_errf(commands, SEDERR_FNTL, commands->linebuf);
+ return -1;
+ }
+ for (i = commands->nfiles - 1; i >= 0; i--)
+ if (strcmp(fnamebuf, commands->fname[i]) == 0) {
+ commands->rep->findex = i;
+ goto done;
+ }
+ if (commands->nfiles >= NWFILES) {
+ command_errf(commands, SEDERR_TMWFMES);
+ return -1;
+ }
+ if ((commands->fname[commands->nfiles] =
+ apr_pstrdup(commands->pool, fnamebuf)) == NULL) {
+ command_errf(commands, SEDERR_OOMMES);
+ return -1;
+ }
+
+ commands->rep->findex = commands->nfiles++;
+ break;
+
+ case 'x':
+ commands->rep->command = XCOM;
+ break;
+
+ case 'y':
+ commands->rep->command = YCOM;
+ commands->sseof = *commands->cp++;
+ commands->rep->re1 = p;
+ p = ycomp(commands, commands->rep->re1);
+ if (p == NULL)
+ return -1;
+ break;
+ }
+done:
+ commands->rep = alloc_reptr(commands);
+
+ commands->rep->ad1 = p;
+
+ if (*commands->cp++ != '\0') {
+ if (commands->cp[-1] == ';')
+ goto comploop;
+ command_errf(commands, SEDERR_CGMES, commands->linebuf);
+ return -1;
+ }
+ }
+ commands->rep->command = 0;
+ commands->lastre = op;
+
+ return 0;
+}
+
+static char *compsub(sed_commands_t *commands,
+ sed_comp_args *compargs, char *rhsbuf)
+{
+ char *p, *q;
+
+ p = rhsbuf;
+ q = commands->cp;
+ for(;;) {
+ if(p > &commands->respace[RESIZE-1]) {
+ command_errf(commands, SEDERR_TMMES, commands->linebuf);
+ return NULL;
+ }
+ if((*p = *q++) == '\\') {
+ p++;
+ if(p > &commands->respace[RESIZE-1]) {
+ command_errf(commands, SEDERR_TMMES, commands->linebuf);
+ return NULL;
+ }
+ *p = *q++;
+ if(*p > compargs->nbra + '0' && *p <= '9') {
+ command_errf(commands, SEDERR_DOORNG, commands->linebuf);
+ return NULL;
+ }
+ p++;
+ continue;
+ }
+ if(*p == commands->sseof) {
+ *p++ = '\0';
+ commands->cp = q;
+ return(p);
+ }
+ if(*p++ == '\0') {
+ command_errf(commands, SEDERR_EDMOSUB, commands->linebuf);
+ return NULL;
+ }
+ }
+}
+
+/*
+ * rline
+ */
+static int rline(sed_commands_t *commands, apr_file_t *fin,
+ char *lbuf, char *lbend)
+{
+ char *p;
+ const char *q;
+ int t;
+ apr_size_t bytes_read;
+
+ p = lbuf;
+
+ if(commands->eflag) {
+ if(commands->eflag > 0) {
+ commands->eflag = -1;
+ q = commands->earg;
+ while((t = *q++) != '\0') {
+ if(t == '\n') {
+ commands->saveq = q;
+ goto out1;
+ }
+ if (p < lbend)
+ *p++ = t;
+ if(t == '\\') {
+ if((t = *q++) == '\0') {
+ commands->saveq = NULL;
+ return(-1);
+ }
+ if (p < lbend)
+ *p++ = t;
+ }
+ }
+ commands->saveq = NULL;
+
+ out1:
+ if (p == lbend) {
+ command_errf(commands, SEDERR_CLTL);
+ return -1;
+ }
+ *p = '\0';
+ return(1);
+ }
+ if((q = commands->saveq) == 0) return(-1);
+
+ while((t = *q++) != '\0') {
+ if(t == '\n') {
+ commands->saveq = q;
+ goto out2;
+ }
+ if(p < lbend)
+ *p++ = t;
+ if(t == '\\') {
+ if((t = *q++) == '\0') {
+ commands->saveq = NULL;
+ return(-1);
+ }
+ if (p < lbend)
+ *p++ = t;
+ }
+ }
+ commands->saveq = NULL;
+
+ out2:
+ if (p == lbend) {
+ command_errf(commands, SEDERR_CLTL);
+ return -1;
+ }
+ *p = '\0';
+ return(1);
+ }
+
+ bytes_read = 1;
+ /* XXX extremely inefficient 1 byte reads */
+ while (apr_file_read(fin, &t, &bytes_read) != APR_SUCCESS) {
+ if(t == '\n') {
+ if (p == lbend) {
+ command_errf(commands, SEDERR_CLTL);
+ return -1;
+ }
+ *p = '\0';
+ return(1);
+ }
+ if (p < lbend)
+ *p++ = t;
+ if(t == '\\') {
+ bytes_read = 1;
+ if (apr_file_read(fin, &t, &bytes_read) != APR_SUCCESS) {
+ return -1;
+ }
+ if(p < lbend)
+ *p++ = t;
+ }
+ bytes_read = 1;
+ }
+ return(-1);
+}
+
+/*
+ * address
+ */
+static char *address(sed_commands_t *commands, char *expbuf,
+ apr_status_t* status)
+{
+ char *rcp;
+ apr_int64_t lno;
+ sed_comp_args compargs;
+
+ *status = APR_SUCCESS;
+ if(*commands->cp == '$') {
+ if (expbuf > &commands->respace[RESIZE-2]) {
+ command_errf(commands, SEDERR_TMMES, commands->linebuf);
+ *status = APR_EGENERAL;
+ return NULL;
+ }
+ commands->cp++;
+ *expbuf++ = CEND;
+ *expbuf++ = CCEOF;
+ return(expbuf);
+ }
+ if (*commands->cp == '/' || *commands->cp == '\\' ) {
+ if ( *commands->cp == '\\' )
+ commands->cp++;
+ commands->sseof = *commands->cp++;
+ return(comple(commands, &compargs, (char *) 0, expbuf, commands->reend,
+ commands->sseof));
+ }
+
+ rcp = commands->cp;
+ lno = 0;
+
+ while(*rcp >= '0' && *rcp <= '9')
+ lno = lno*10 + *rcp++ - '0';
+
+ if(rcp > commands->cp) {
+ if (expbuf > &commands->respace[RESIZE-3]) {
+ command_errf(commands, SEDERR_TMMES, commands->linebuf);
+ *status = APR_EGENERAL;
+ return NULL;
+ }
+ *expbuf++ = CLNUM;
+ *expbuf++ = commands->nlno;
+ commands->tlno[commands->nlno++] = lno;
+ if(commands->nlno >= SED_NLINES) {
+ command_errf(commands, SEDERR_TMLNMES);
+ *status = APR_EGENERAL;
+ return NULL;
+ }
+ *expbuf++ = CCEOF;
+ commands->cp = rcp;
+ return(expbuf);
+ }
+ return(NULL);
+}
+
+/*
+ * text
+ */
+static char *text(sed_commands_t *commands, char *textbuf, char *tbend)
+{
+ char *p, *q;
+
+ p = textbuf;
+ q = commands->cp;
+#ifndef S5EMUL
+ /*
+ * Strip off indentation from text to be inserted.
+ */
+ while(*q == '\t' || *q == ' ') q++;
+#endif
+ for(;;) {
+
+ if(p > tbend)
+ return(NULL); /* overflowed the buffer */
+ if((*p = *q++) == '\\')
+ *p = *q++;
+ if(*p == '\0') {
+ commands->cp = --q;
+ return(++p);
+ }
+#ifndef S5EMUL
+ /*
+ * Strip off indentation from text to be inserted.
+ */
+ if(*p == '\n') {
+ while(*q == '\t' || *q == ' ') q++;
+ }
+#endif
+ p++;
+ }
+}
+
+
+/*
+ * search
+ */
+static sed_label_t *search(sed_commands_t *commands)
+{
+ sed_label_t *rp;
+ sed_label_t *ptr;
+
+ rp = commands->labtab;
+ ptr = commands->lab;
+ while (rp < ptr) {
+ if (strcmp(rp->asc, ptr->asc) == 0)
+ return rp;
+ rp++;
+ }
+
+ return 0;
+}
+
+/*
+ * ycomp
+ */
+static char *ycomp(sed_commands_t *commands, char *expbuf)
+{
+ char c;
+ int cint; /* integer value of char c */
+ char *ep, *tsp;
+ int i;
+ char *sp;
+
+ ep = expbuf;
+ if(ep + 0377 > &commands->respace[RESIZE-1]) {
+ command_errf(commands, SEDERR_TMMES, commands->linebuf);
+ return NULL;
+ }
+ sp = commands->cp;
+ for(tsp = commands->cp; (c = *tsp) != commands->sseof; tsp++) {
+ if(c == '\\')
+ tsp++;
+ if(c == '\0' || c == '\n') {
+ command_errf(commands, SEDERR_EDMOSTR, commands->linebuf);
+ return NULL;
+ }
+ }
+ tsp++;
+ memset(ep, 0, 0400);
+
+ while((c = *sp++) != commands->sseof) {
+ c &= 0377;
+ if(c == '\\' && *sp == 'n') {
+ sp++;
+ c = '\n';
+ }
+ cint = (int) c;
+ if((ep[cint] = *tsp++) == '\\' && *tsp == 'n') {
+ ep[cint] = '\n';
+ tsp++;
+ }
+ if(ep[cint] == commands->sseof || ep[cint] == '\0') {
+ command_errf(commands, SEDERR_TSNTSS, commands->linebuf);
+ }
+ }
+ if(*tsp != commands->sseof) {
+ if(*tsp == '\0') {
+ command_errf(commands, SEDERR_EDMOSTR, commands->linebuf);
+ }
+ else {
+ command_errf(commands, SEDERR_TSNTSS, commands->linebuf);
+ }
+ return NULL;
+ }
+ commands->cp = ++tsp;
+
+ for(i = 0; i < 0400; i++)
+ if(ep[i] == 0)
+ ep[i] = i;
+
+ return(ep + 0400);
+}
+
+/*
+ * comple
+ */
+static char *comple(sed_commands_t *commands, sed_comp_args *compargs,
+ char *x1, char *ep, char *x3, char x4)
+{
+ char *p;
+
+ p = sed_compile(commands, compargs, ep + 1, x3, x4);
+ if(p == ep + 1)
+ return(ep);
+ *ep = compargs->circf;
+ return(p);
+}
+
+/*
+ * alloc_reptr
+ */
+static sed_reptr_t *alloc_reptr(sed_commands_t *commands)
+{
+ sed_reptr_t *var;
+
+ var = apr_pcalloc(commands->pool, sizeof(sed_reptr_t));
+ if (var == NULL) {
+ command_errf(commands, SEDERR_OOMMES);
+ return 0;
+ }
+
+ var->nrep = commands->nrep;
+ var->findex = -1;
+ commands->nrep++;
+
+ if (commands->ptrspace == NULL)
+ commands->ptrspace = var;
+ else
+ commands->ptrend->next = var;
+
+ commands->ptrend = var;
+ commands->labtab->address = var;
+ return var;
+}
+
+
diff --git a/modules/filters/sed1.c b/modules/filters/sed1.c
new file mode 100644
index 0000000..047f49b
--- /dev/null
+++ b/modules/filters/sed1.c
@@ -0,0 +1,1110 @@
+/*
+ * Copyright (c) 2005, 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1984 AT&T
+ * All Rights Reserved
+ *
+ * Licensed 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.
+ */
+
+#include "apr.h"
+#include "apr_lib.h"
+#include "libsed.h"
+#include "sed.h"
+#include "apr_strings.h"
+#include "regexp.h"
+
+static const char *const trans[040] = {
+ "\\01",
+ "\\02",
+ "\\03",
+ "\\04",
+ "\\05",
+ "\\06",
+ "\\07",
+ "\\10",
+ "\\11",
+ "\n",
+ "\\13",
+ "\\14",
+ "\\15",
+ "\\16",
+ "\\17",
+ "\\20",
+ "\\21",
+ "\\22",
+ "\\23",
+ "\\24",
+ "\\25",
+ "\\26",
+ "\\27",
+ "\\30",
+ "\\31",
+ "\\32",
+ "\\33",
+ "\\34",
+ "\\35",
+ "\\36",
+ "\\37"
+};
+static const char rub[] = {"\\177"};
+
+extern int sed_step(char *p1, char *p2, int circf, step_vars_storage *vars);
+static int substitute(sed_eval_t *eval, sed_reptr_t *ipc,
+ step_vars_storage *step_vars);
+static apr_status_t execute(sed_eval_t *eval);
+static int match(sed_eval_t *eval, char *expbuf, int gf,
+ step_vars_storage *step_vars);
+static apr_status_t dosub(sed_eval_t *eval, char *rhsbuf, int n,
+ step_vars_storage *step_vars);
+static char *place(sed_eval_t *eval, char *asp, char *al1, char *al2);
+static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc,
+ step_vars_storage *step_vars);
+static apr_status_t wline(sed_eval_t *eval, char *buf, apr_size_t sz);
+static apr_status_t arout(sed_eval_t *eval);
+
+static void eval_errf(sed_eval_t *eval, const char *fmt, ...)
+{
+ if (eval->errfn && eval->pool) {
+ va_list args;
+ const char* error;
+ va_start(args, fmt);
+ error = apr_pvsprintf(eval->pool, fmt, args);
+ eval->errfn(eval->data, error);
+ va_end(args);
+ }
+}
+
+#define INIT_BUF_SIZE 1024
+#define MAX_BUF_SIZE 1024*8192
+
+/*
+ * grow_buffer
+ */
+static apr_status_t grow_buffer(apr_pool_t *pool, char **buffer,
+ char **spend, apr_size_t *cursize,
+ apr_size_t newsize)
+{
+ char* newbuffer = NULL;
+ apr_size_t spendsize = 0;
+ if (*cursize >= newsize) {
+ return APR_SUCCESS;
+ }
+ /* Avoid number of times realloc is called. It could cause huge memory
+ * requirement if line size is huge e.g 2 MB */
+ if (newsize < *cursize * 2) {
+ newsize = *cursize * 2;
+ }
+
+ /* Align it to 4 KB boundary */
+ newsize = (newsize + ((1 << 12) - 1)) & ~((1 << 12) - 1);
+ if (newsize > MAX_BUF_SIZE) {
+ return APR_ENOMEM;
+ }
+ newbuffer = apr_pcalloc(pool, newsize);
+ if (*spend && *buffer && (*cursize > 0)) {
+ spendsize = *spend - *buffer;
+ }
+ if ((*cursize > 0) && *buffer) {
+ memcpy(newbuffer, *buffer, *cursize);
+ }
+ *buffer = newbuffer;
+ *cursize = newsize;
+ if (spend != buffer) {
+ *spend = *buffer + spendsize;
+ }
+ return APR_SUCCESS;
+}
+
+/*
+ * grow_line_buffer
+ */
+static apr_status_t grow_line_buffer(sed_eval_t *eval, apr_size_t newsize)
+{
+ return grow_buffer(eval->pool, &eval->linebuf, &eval->lspend,
+ &eval->lsize, newsize);
+}
+
+/*
+ * grow_hold_buffer
+ */
+static apr_status_t grow_hold_buffer(sed_eval_t *eval, apr_size_t newsize)
+{
+ return grow_buffer(eval->pool, &eval->holdbuf, &eval->hspend,
+ &eval->hsize, newsize);
+}
+
+/*
+ * grow_gen_buffer
+ */
+static apr_status_t grow_gen_buffer(sed_eval_t *eval, apr_size_t newsize,
+ char **gspend)
+{
+ apr_status_t rc = 0;
+ if (gspend == NULL) {
+ gspend = &eval->genbuf;
+ }
+ rc = grow_buffer(eval->pool, &eval->genbuf, gspend,
+ &eval->gsize, newsize);
+ if (rc == APR_SUCCESS) {
+ eval->lcomend = &eval->genbuf[71];
+ }
+ return rc;
+}
+
+/*
+ * appendmem_to_linebuf
+ */
+static apr_status_t appendmem_to_linebuf(sed_eval_t *eval, const char* sz, apr_size_t len)
+{
+ apr_status_t rc = 0;
+ apr_size_t reqsize = (eval->lspend - eval->linebuf) + len;
+ if (eval->lsize < reqsize) {
+ rc = grow_line_buffer(eval, reqsize);
+ if (rc != APR_SUCCESS) {
+ return rc;
+ }
+ }
+ memcpy(eval->lspend, sz, len);
+ eval->lspend += len;
+ return APR_SUCCESS;
+}
+
+/*
+ * append_to_linebuf
+ */
+static apr_status_t append_to_linebuf(sed_eval_t *eval, const char* sz,
+ step_vars_storage *step_vars)
+{
+ apr_size_t len = strlen(sz);
+ char *old_linebuf = eval->linebuf;
+ apr_status_t rc = 0;
+ /* Copy string including null character */
+ rc = appendmem_to_linebuf(eval, sz, len + 1);
+ if (rc != APR_SUCCESS) {
+ return rc;
+ }
+ --eval->lspend; /* lspend will now point to NULL character */
+ /* Sync step_vars after a possible linebuf expansion */
+ if (step_vars && old_linebuf != eval->linebuf) {
+ if (step_vars->loc1) {
+ step_vars->loc1 = step_vars->loc1 - old_linebuf + eval->linebuf;
+ }
+ if (step_vars->loc2) {
+ step_vars->loc2 = step_vars->loc2 - old_linebuf + eval->linebuf;
+ }
+ if (step_vars->locs) {
+ step_vars->locs = step_vars->locs - old_linebuf + eval->linebuf;
+ }
+ }
+ return APR_SUCCESS;
+}
+
+/*
+ * copy_to_linebuf
+ */
+static apr_status_t copy_to_linebuf(sed_eval_t *eval, const char* sz,
+ step_vars_storage *step_vars)
+{
+ eval->lspend = eval->linebuf;
+ return append_to_linebuf(eval, sz, step_vars);
+}
+
+/*
+ * append_to_holdbuf
+ */
+static apr_status_t append_to_holdbuf(sed_eval_t *eval, const char* sz)
+{
+ apr_size_t len = strlen(sz);
+ apr_size_t reqsize = (eval->hspend - eval->holdbuf) + len + 1;
+ apr_status_t rc = 0;
+ if (eval->hsize <= reqsize) {
+ rc = grow_hold_buffer(eval, reqsize);
+ if (rc != APR_SUCCESS) {
+ return rc;
+ }
+ }
+ memcpy(eval->hspend, sz, len + 1);
+ /* hspend will now point to NULL character */
+ eval->hspend += len;
+ return APR_SUCCESS;
+}
+
+/*
+ * copy_to_holdbuf
+ */
+static apr_status_t copy_to_holdbuf(sed_eval_t *eval, const char* sz)
+{
+ eval->hspend = eval->holdbuf;
+ return append_to_holdbuf(eval, sz);
+}
+
+/*
+ * append_to_genbuf
+ */
+static apr_status_t append_to_genbuf(sed_eval_t *eval, const char* sz, char **gspend)
+{
+ apr_size_t len = strlen(sz);
+ apr_size_t reqsize = (*gspend - eval->genbuf) + len + 1;
+ apr_status_t rc = 0;
+ if (eval->gsize < reqsize) {
+ rc = grow_gen_buffer(eval, reqsize, gspend);
+ if (rc != APR_SUCCESS) {
+ return rc;
+ }
+ }
+ memcpy(*gspend, sz, len + 1);
+ /* *gspend will now point to NULL character */
+ *gspend += len;
+ return APR_SUCCESS;
+}
+
+/*
+ * copy_to_genbuf
+ */
+static apr_status_t copy_to_genbuf(sed_eval_t *eval, const char* sz)
+{
+ apr_size_t len = strlen(sz);
+ apr_size_t reqsize = len + 1;
+ apr_status_t rc = APR_SUCCESS;;
+ if (eval->gsize < reqsize) {
+ rc = grow_gen_buffer(eval, reqsize, NULL);
+ if (rc != APR_SUCCESS) {
+ return rc;
+ }
+ }
+ memcpy(eval->genbuf, sz, len + 1);
+ return rc;
+}
+
+/*
+ * sed_init_eval
+ */
+apr_status_t sed_init_eval(sed_eval_t *eval, sed_commands_t *commands, sed_err_fn_t *errfn, void *data, sed_write_fn_t *writefn, apr_pool_t* p)
+{
+ memset(eval, 0, sizeof(*eval));
+ eval->pool = p;
+ eval->writefn = writefn;
+ return sed_reset_eval(eval, commands, errfn, data);
+}
+
+/*
+ * sed_reset_eval
+ */
+apr_status_t sed_reset_eval(sed_eval_t *eval, sed_commands_t *commands, sed_err_fn_t *errfn, void *data)
+{
+ int i;
+
+ eval->errfn = errfn;
+ eval->data = data;
+
+ eval->commands = commands;
+
+ eval->lnum = 0;
+ eval->fout = NULL;
+
+ if (eval->linebuf == NULL) {
+ eval->lsize = INIT_BUF_SIZE;
+ eval->linebuf = apr_pcalloc(eval->pool, eval->lsize);
+ }
+ if (eval->holdbuf == NULL) {
+ eval->hsize = INIT_BUF_SIZE;
+ eval->holdbuf = apr_pcalloc(eval->pool, eval->hsize);
+ }
+ if (eval->genbuf == NULL) {
+ eval->gsize = INIT_BUF_SIZE;
+ eval->genbuf = apr_pcalloc(eval->pool, eval->gsize);
+ }
+ eval->lspend = eval->linebuf;
+ eval->hspend = eval->holdbuf;
+ eval->lcomend = &eval->genbuf[71];
+
+ for (i = 0; i < sizeof(eval->abuf) / sizeof(eval->abuf[0]); i++)
+ eval->abuf[i] = NULL;
+ eval->aptr = eval->abuf;
+ eval->pending = NULL;
+ eval->inar = apr_pcalloc(eval->pool, commands->nrep * sizeof(unsigned char));
+ eval->nrep = commands->nrep;
+
+ eval->dolflag = 0;
+ eval->sflag = 0;
+ eval->jflag = 0;
+ eval->delflag = 0;
+ eval->lreadyflag = 0;
+ eval->quitflag = 0;
+ eval->finalflag = 1; /* assume we're evaluating only one file/stream */
+ eval->numpass = 0;
+ eval->nullmatch = 0;
+ eval->col = 0;
+
+ for (i = 0; i < commands->nfiles; i++) {
+ const char* filename = commands->fname[i];
+ if (apr_file_open(&eval->fcode[i], filename,
+ APR_WRITE | APR_CREATE, APR_OS_DEFAULT,
+ eval->pool) != APR_SUCCESS) {
+ eval_errf(eval, SEDERR_COMES, filename);
+ return APR_EGENERAL;
+ }
+ }
+
+ return APR_SUCCESS;
+}
+
+/*
+ * sed_destroy_eval
+ */
+void sed_destroy_eval(sed_eval_t *eval)
+{
+ int i;
+ /* eval->linebuf, eval->holdbuf, eval->genbuf and eval->inar are allocated
+ * on pool. It will be freed when pool will be freed */
+ for (i = 0; i < eval->commands->nfiles; i++) {
+ if (eval->fcode[i] != NULL) {
+ apr_file_close(eval->fcode[i]);
+ eval->fcode[i] = NULL;
+ }
+ }
+}
+
+/*
+ * sed_eval_file
+ */
+apr_status_t sed_eval_file(sed_eval_t *eval, apr_file_t *fin, void *fout)
+{
+ for (;;) {
+ char buf[1024];
+ apr_size_t read_bytes = 0;
+
+ read_bytes = sizeof(buf);
+ if (apr_file_read(fin, buf, &read_bytes) != APR_SUCCESS)
+ break;
+
+ if (sed_eval_buffer(eval, buf, read_bytes, fout) != APR_SUCCESS)
+ return APR_EGENERAL;
+
+ if (eval->quitflag)
+ return APR_SUCCESS;
+ }
+
+ return sed_finalize_eval(eval, fout);
+}
+
+/*
+ * sed_eval_buffer
+ */
+apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, apr_size_t bufsz, void *fout)
+{
+ apr_status_t rv;
+
+ if (eval->quitflag)
+ return APR_SUCCESS;
+
+ if (!sed_canbe_finalized(eval->commands)) {
+ /* Commands were not finalized properly. */
+ const char* error = sed_get_finalize_error(eval->commands, eval->pool);
+ if (error) {
+ eval_errf(eval, error);
+ return APR_EGENERAL;
+ }
+ }
+
+ eval->fout = fout;
+
+ /* Process leftovers */
+ if (bufsz && eval->lreadyflag) {
+ eval->lreadyflag = 0;
+ eval->lspend--;
+ *eval->lspend = '\0';
+ rv = execute(eval);
+ if (rv != APR_SUCCESS)
+ return rv;
+ }
+
+ while (bufsz) {
+ apr_status_t rc = 0;
+ char *n;
+ apr_size_t llen;
+
+ n = memchr(buf, '\n', bufsz);
+ if (n == NULL)
+ break;
+
+ llen = n - buf;
+ if (llen == bufsz - 1) {
+ /* This might be the last line; delay its processing */
+ eval->lreadyflag = 1;
+ break;
+ }
+
+ rc = appendmem_to_linebuf(eval, buf, llen + 1);
+ if (rc != APR_SUCCESS) {
+ return rc;
+ }
+ --eval->lspend;
+ /* replace new line character with NULL */
+ *eval->lspend = '\0';
+ buf += (llen + 1);
+ bufsz -= (llen + 1);
+ rv = execute(eval);
+ if (rv != APR_SUCCESS)
+ return rv;
+ if (eval->quitflag)
+ break;
+ }
+
+ /* Save the leftovers for later */
+ if (bufsz) {
+ apr_status_t rc = appendmem_to_linebuf(eval, buf, bufsz);
+ if (rc != APR_SUCCESS) {
+ return rc;
+ }
+ }
+
+ return APR_SUCCESS;
+}
+
+/*
+ * sed_finalize_eval
+ */
+apr_status_t sed_finalize_eval(sed_eval_t *eval, void *fout)
+{
+ if (eval->quitflag)
+ return APR_SUCCESS;
+
+ if (eval->finalflag)
+ eval->dolflag = 1;
+
+ eval->fout = fout;
+
+ /* Process leftovers */
+ if (eval->lspend > eval->linebuf) {
+ apr_status_t rv;
+ apr_status_t rc = 0;
+
+ if (eval->lreadyflag) {
+ eval->lreadyflag = 0;
+ eval->lspend--;
+ } else {
+ /* Code can probably reach here when last character in output
+ * buffer is not a newline.
+ */
+ /* Assure space for NULL */
+ rc = append_to_linebuf(eval, "", NULL);
+ if (rc != APR_SUCCESS) {
+ return rc;
+ }
+ }
+
+ *eval->lspend = '\0';
+ rv = execute(eval);
+ if (rv != APR_SUCCESS)
+ return rv;
+ }
+
+ eval->quitflag = 1;
+
+ return APR_SUCCESS;
+}
+
+/*
+ * execute
+ */
+static apr_status_t execute(sed_eval_t *eval)
+{
+ sed_reptr_t *ipc = eval->commands->ptrspace;
+ step_vars_storage step_vars;
+ apr_status_t rv = APR_SUCCESS;
+
+ eval->lnum++;
+
+ eval->sflag = 0;
+
+ if (eval->pending) {
+ ipc = eval->pending;
+ eval->pending = NULL;
+ }
+
+ memset(&step_vars, 0, sizeof(step_vars));
+
+ while (ipc->command) {
+ char *p1;
+ char *p2;
+ int c;
+
+ p1 = ipc->ad1;
+ p2 = ipc->ad2;
+
+ if (p1) {
+
+ if (eval->inar[ipc->nrep]) {
+ if (*p2 == CEND) {
+ p1 = 0;
+ } else if (*p2 == CLNUM) {
+ c = (unsigned char)p2[1];
+ if (eval->lnum > eval->commands->tlno[c]) {
+ eval->inar[ipc->nrep] = 0;
+ if (ipc->negfl)
+ goto yes;
+ ipc = ipc->next;
+ continue;
+ }
+ if (eval->lnum == eval->commands->tlno[c]) {
+ eval->inar[ipc->nrep] = 0;
+ }
+ } else if (match(eval, p2, 0, &step_vars)) {
+ eval->inar[ipc->nrep] = 0;
+ }
+ } else if (*p1 == CEND) {
+ if (!eval->dolflag) {
+ if (ipc->negfl)
+ goto yes;
+ ipc = ipc->next;
+ continue;
+ }
+ } else if (*p1 == CLNUM) {
+ c = (unsigned char)p1[1];
+ if (eval->lnum != eval->commands->tlno[c]) {
+ if (ipc->negfl)
+ goto yes;
+ ipc = ipc->next;
+ continue;
+ }
+ if (p2)
+ eval->inar[ipc->nrep] = 1;
+ } else if (match(eval, p1, 0, &step_vars)) {
+ if (p2)
+ eval->inar[ipc->nrep] = 1;
+ } else {
+ if (ipc->negfl)
+ goto yes;
+ ipc = ipc->next;
+ continue;
+ }
+ }
+
+ if (ipc->negfl) {
+ ipc = ipc->next;
+ continue;
+ }
+
+yes:
+ rv = command(eval, ipc, &step_vars);
+ if (rv != APR_SUCCESS)
+ return rv;
+
+ if (eval->quitflag)
+ return APR_SUCCESS;
+
+ if (eval->pending)
+ return APR_SUCCESS;
+
+ if (eval->delflag)
+ break;
+
+ if (eval->jflag) {
+ eval->jflag = 0;
+ if ((ipc = ipc->lb1) == 0) {
+ ipc = eval->commands->ptrspace;
+ break;
+ }
+ } else
+ ipc = ipc->next;
+ }
+
+ if (!eval->commands->nflag && !eval->delflag) {
+ rv = wline(eval, eval->linebuf, eval->lspend - eval->linebuf);
+ if (rv != APR_SUCCESS)
+ return rv;
+ }
+
+ if (eval->aptr > eval->abuf)
+ rv = arout(eval);
+
+ eval->delflag = 0;
+
+ eval->lspend = eval->linebuf;
+
+ return rv;
+}
+
+/*
+ * match
+ */
+static int match(sed_eval_t *eval, char *expbuf, int gf,
+ step_vars_storage *step_vars)
+{
+ char *p1;
+ int circf;
+
+ if (gf) {
+ if (*expbuf) return(0);
+ step_vars->locs = p1 = step_vars->loc2;
+ } else {
+ p1 = eval->linebuf;
+ step_vars->locs = 0;
+ }
+
+ circf = *expbuf++;
+ return(sed_step(p1, expbuf, circf, step_vars));
+}
+
+/*
+ * substitute
+ */
+static int substitute(sed_eval_t *eval, sed_reptr_t *ipc,
+ step_vars_storage *step_vars)
+{
+ if (match(eval, ipc->re1, 0, step_vars) == 0) return(0);
+
+ eval->numpass = 0;
+ eval->sflag = 0; /* Flags if any substitution was made */
+ if (dosub(eval, ipc->rhs, ipc->gfl, step_vars) != APR_SUCCESS)
+ return -1;
+
+ if (ipc->gfl) {
+ while (*step_vars->loc2) {
+ if (match(eval, ipc->re1, 1, step_vars) == 0) break;
+ if (dosub(eval, ipc->rhs, ipc->gfl, step_vars) != APR_SUCCESS)
+ return -1;
+ }
+ }
+ return(eval->sflag);
+}
+
+/*
+ * dosub
+ */
+static apr_status_t dosub(sed_eval_t *eval, char *rhsbuf, int n,
+ step_vars_storage *step_vars)
+{
+ char *lp, *sp, *rp;
+ int c;
+ apr_status_t rv = APR_SUCCESS;
+
+ if (n > 0 && n < 999) {
+ eval->numpass++;
+ if (n != eval->numpass) return APR_SUCCESS;
+ }
+ eval->sflag = 1;
+ lp = eval->linebuf;
+ sp = eval->genbuf;
+ rp = rhsbuf;
+ sp = place(eval, sp, lp, step_vars->loc1);
+ if (sp == NULL) {
+ return APR_EGENERAL;
+ }
+ while ((c = *rp++) != 0) {
+ if (c == '&') {
+ sp = place(eval, sp, step_vars->loc1, step_vars->loc2);
+ if (sp == NULL) {
+ return APR_EGENERAL;
+ }
+ }
+ else if (c == '\\') {
+ c = *rp++;
+ if (c >= '1' && c < NBRA+'1') {
+ sp = place(eval, sp, step_vars->braslist[c-'1'],
+ step_vars->braelist[c-'1']);
+ if (sp == NULL)
+ return APR_EGENERAL;
+ }
+ else
+ *sp++ = c;
+ } else
+ *sp++ = c;
+ if (sp >= eval->genbuf + eval->gsize) {
+ /* expand genbuf and set the sp appropriately */
+ rv = grow_gen_buffer(eval, eval->gsize + 1024, &sp);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ }
+ }
+ lp = step_vars->loc2;
+ step_vars->loc2 = sp - eval->genbuf + eval->linebuf;
+ rv = append_to_genbuf(eval, lp, &sp);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ rv = copy_to_linebuf(eval, eval->genbuf, step_vars);
+ return rv;
+}
+
+/*
+ * place
+ */
+static char *place(sed_eval_t *eval, char *asp, char *al1, char *al2)
+{
+ char *sp = asp;
+ apr_size_t n = al2 - al1;
+ apr_size_t reqsize = (sp - eval->genbuf) + n + 1;
+
+ if (eval->gsize < reqsize) {
+ apr_status_t rc = grow_gen_buffer(eval, reqsize, &sp);
+ if (rc != APR_SUCCESS) {
+ return NULL;
+ }
+ }
+ memcpy(sp, al1, n);
+ return sp + n;
+}
+
+/*
+ * command
+ */
+static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc,
+ step_vars_storage *step_vars)
+{
+ int i;
+ char *p1, *p2;
+ const char *p3;
+ int length;
+ char sz[32]; /* 32 bytes enough to store 64 bit integer in decimal */
+ apr_status_t rv = APR_SUCCESS;
+
+
+ switch(ipc->command) {
+
+ case ACOM:
+ if (eval->aptr >= &eval->abuf[SED_ABUFSIZE]) {
+ eval_errf(eval, SEDERR_TMAMES, eval->lnum);
+ } else {
+ *eval->aptr++ = ipc;
+ *eval->aptr = NULL;
+ }
+ break;
+
+ case CCOM:
+ eval->delflag = 1;
+ if (!eval->inar[ipc->nrep] || eval->dolflag) {
+ for (p1 = ipc->re1; *p1; p1++)
+ ;
+ rv = wline(eval, ipc->re1, p1 - ipc->re1);
+ }
+ break;
+
+ case DCOM:
+ eval->delflag++;
+ break;
+
+ case CDCOM:
+ p1 = eval->linebuf;
+
+ while (*p1 != '\n') {
+ if (*p1++ == 0) {
+ eval->delflag++;
+ return APR_SUCCESS;
+ }
+ }
+
+ p1++;
+ rv = copy_to_linebuf(eval, p1, step_vars);
+ if (rv != APR_SUCCESS) return rv;
+ eval->jflag++;
+ break;
+
+ case EQCOM:
+ length = apr_snprintf(sz, sizeof(sz), "%d", (int) eval->lnum);
+ rv = wline(eval, sz, length);
+ break;
+
+ case GCOM:
+ rv = copy_to_linebuf(eval, eval->holdbuf, step_vars);
+ if (rv != APR_SUCCESS) return rv;
+ break;
+
+ case CGCOM:
+ rv = append_to_linebuf(eval, "\n", step_vars);
+ if (rv != APR_SUCCESS) return rv;
+ rv = append_to_linebuf(eval, eval->holdbuf, step_vars);
+ if (rv != APR_SUCCESS) return rv;
+ break;
+
+ case HCOM:
+ rv = copy_to_holdbuf(eval, eval->linebuf);
+ if (rv != APR_SUCCESS) return rv;
+ break;
+
+ case CHCOM:
+ rv = append_to_holdbuf(eval, "\n");
+ if (rv != APR_SUCCESS) return rv;
+ rv = append_to_holdbuf(eval, eval->linebuf);
+ if (rv != APR_SUCCESS) return rv;
+ break;
+
+ case ICOM:
+ for (p1 = ipc->re1; *p1; p1++);
+ rv = wline(eval, ipc->re1, p1 - ipc->re1);
+ break;
+
+ case BCOM:
+ eval->jflag = 1;
+ break;
+
+ case LCOM:
+ p1 = eval->linebuf;
+ p2 = eval->genbuf;
+ eval->genbuf[72] = 0;
+ while (*p1) {
+ if ((unsigned char)*p1 >= 040) {
+ if (*p1 == 0177) {
+ p3 = rub;
+ while ((*p2++ = *p3++) != 0)
+ if (p2 >= eval->lcomend) {
+ *p2 = '\\';
+ rv = wline(eval, eval->genbuf,
+ strlen(eval->genbuf));
+ if (rv != APR_SUCCESS)
+ return rv;
+ p2 = eval->genbuf;
+ }
+ p2--;
+ p1++;
+ continue;
+ }
+ if (!isprint(*p1 & 0377)) {
+ *p2++ = '\\';
+ if (p2 >= eval->lcomend) {
+ *p2 = '\\';
+ rv = wline(eval, eval->genbuf,
+ strlen(eval->genbuf));
+ if (rv != APR_SUCCESS)
+ return rv;
+ p2 = eval->genbuf;
+ }
+ *p2++ = (*p1 >> 6) + '0';
+ if (p2 >= eval->lcomend) {
+ *p2 = '\\';
+ rv = wline(eval, eval->genbuf,
+ strlen(eval->genbuf));
+ if (rv != APR_SUCCESS)
+ return rv;
+ p2 = eval->genbuf;
+ }
+ *p2++ = ((*p1 >> 3) & 07) + '0';
+ if (p2 >= eval->lcomend) {
+ *p2 = '\\';
+ rv = wline(eval, eval->genbuf,
+ strlen(eval->genbuf));
+ if (rv != APR_SUCCESS)
+ return rv;
+ p2 = eval->genbuf;
+ }
+ *p2++ = (*p1++ & 07) + '0';
+ if (p2 >= eval->lcomend) {
+ *p2 = '\\';
+ rv = wline(eval, eval->genbuf,
+ strlen(eval->genbuf));
+ if (rv != APR_SUCCESS)
+ return rv;
+ p2 = eval->genbuf;
+ }
+ } else {
+ *p2++ = *p1++;
+ if (p2 >= eval->lcomend) {
+ *p2 = '\\';
+ rv = wline(eval, eval->genbuf,
+ strlen(eval->genbuf));
+ if (rv != APR_SUCCESS)
+ return rv;
+ p2 = eval->genbuf;
+ }
+ }
+ } else {
+ p3 = trans[(unsigned char)*p1-1];
+ while ((*p2++ = *p3++) != 0)
+ if (p2 >= eval->lcomend) {
+ *p2 = '\\';
+ rv = wline(eval, eval->genbuf,
+ strlen(eval->genbuf));
+ if (rv != APR_SUCCESS)
+ return rv;
+ p2 = eval->genbuf;
+ }
+ p2--;
+ p1++;
+ }
+ }
+ *p2 = 0;
+ rv = wline(eval, eval->genbuf, strlen(eval->genbuf));
+ break;
+
+ case NCOM:
+ if (!eval->commands->nflag) {
+ rv = wline(eval, eval->linebuf, eval->lspend - eval->linebuf);
+ if (rv != APR_SUCCESS)
+ return rv;
+ }
+
+ if (eval->aptr > eval->abuf) {
+ rv = arout(eval);
+ if (rv != APR_SUCCESS)
+ return rv;
+ }
+ eval->lspend = eval->linebuf;
+ eval->pending = ipc->next;
+ break;
+
+ case CNCOM:
+ if (eval->aptr > eval->abuf) {
+ rv = arout(eval);
+ if (rv != APR_SUCCESS)
+ return rv;
+ }
+ rv = append_to_linebuf(eval, "\n", step_vars);
+ if (rv != APR_SUCCESS) return rv;
+ eval->pending = ipc->next;
+ break;
+
+ case PCOM:
+ rv = wline(eval, eval->linebuf, eval->lspend - eval->linebuf);
+ break;
+
+ case CPCOM:
+ for (p1 = eval->linebuf; *p1 != '\n' && *p1 != '\0'; p1++);
+ rv = wline(eval, eval->linebuf, p1 - eval->linebuf);
+ break;
+
+ case QCOM:
+ if (!eval->commands->nflag) {
+ rv = wline(eval, eval->linebuf, eval->lspend - eval->linebuf);
+ if (rv != APR_SUCCESS)
+ break;
+ }
+
+ if (eval->aptr > eval->abuf) {
+ rv = arout(eval);
+ if (rv != APR_SUCCESS)
+ return rv;
+ }
+
+ eval->quitflag = 1;
+ break;
+
+ case RCOM:
+ if (eval->aptr >= &eval->abuf[SED_ABUFSIZE]) {
+ eval_errf(eval, SEDERR_TMRMES, eval->lnum);
+ } else {
+ *eval->aptr++ = ipc;
+ *eval->aptr = NULL;
+ }
+ break;
+
+ case SCOM:
+ i = substitute(eval, ipc, step_vars);
+ if (i == -1) {
+ return APR_EGENERAL;
+ }
+ if (ipc->pfl && eval->commands->nflag && i) {
+ if (ipc->pfl == 1) {
+ rv = wline(eval, eval->linebuf, eval->lspend -
+ eval->linebuf);
+ if (rv != APR_SUCCESS)
+ return rv;
+ } else {
+ for (p1 = eval->linebuf; *p1 != '\n' && *p1 != '\0'; p1++);
+ rv = wline(eval, eval->linebuf, p1 - eval->linebuf);
+ if (rv != APR_SUCCESS)
+ return rv;
+ }
+ }
+ if (i && (ipc->findex >= 0) && eval->fcode[ipc->findex])
+ apr_file_printf(eval->fcode[ipc->findex], "%s\n",
+ eval->linebuf);
+ break;
+
+ case TCOM:
+ if (eval->sflag == 0) break;
+ eval->sflag = 0;
+ eval->jflag = 1;
+ break;
+
+ case WCOM:
+ if (ipc->findex >= 0)
+ apr_file_printf(eval->fcode[ipc->findex], "%s\n",
+ eval->linebuf);
+ break;
+
+ case XCOM:
+ rv = copy_to_genbuf(eval, eval->linebuf);
+ if (rv != APR_SUCCESS) return rv;
+ rv = copy_to_linebuf(eval, eval->holdbuf, step_vars);
+ if (rv != APR_SUCCESS) return rv;
+ rv = copy_to_holdbuf(eval, eval->genbuf);
+ if (rv != APR_SUCCESS) return rv;
+ break;
+
+ case YCOM:
+ p1 = eval->linebuf;
+ p2 = ipc->re1;
+ while ((*p1 = p2[(unsigned char)*p1]) != 0) p1++;
+ break;
+ }
+ return rv;
+}
+
+/*
+ * arout
+ */
+static apr_status_t arout(sed_eval_t *eval)
+{
+ apr_status_t rv = APR_SUCCESS;
+ eval->aptr = eval->abuf - 1;
+ while (*++eval->aptr) {
+ if ((*eval->aptr)->command == ACOM) {
+ char *p1;
+
+ for (p1 = (*eval->aptr)->re1; *p1; p1++);
+ rv = wline(eval, (*eval->aptr)->re1, p1 - (*eval->aptr)->re1);
+ if (rv != APR_SUCCESS)
+ return rv;
+ } else {
+ apr_file_t *fi = NULL;
+ char buf[512];
+ apr_size_t n = sizeof(buf);
+
+ if (apr_file_open(&fi, (*eval->aptr)->re1, APR_READ, 0, eval->pool)
+ != APR_SUCCESS)
+ continue;
+ while ((apr_file_read(fi, buf, &n)) == APR_SUCCESS) {
+ if (n == 0)
+ break;
+ rv = eval->writefn(eval->fout, buf, n);
+ if (rv != APR_SUCCESS) {
+ apr_file_close(fi);
+ return rv;
+ }
+ n = sizeof(buf);
+ }
+ apr_file_close(fi);
+ }
+ }
+ eval->aptr = eval->abuf;
+ *eval->aptr = NULL;
+ return rv;
+}
+
+/*
+ * wline
+ */
+static apr_status_t wline(sed_eval_t *eval, char *buf, apr_size_t sz)
+{
+ apr_status_t rv = APR_SUCCESS;
+ rv = eval->writefn(eval->fout, buf, sz);
+ if (rv != APR_SUCCESS)
+ return rv;
+ rv = eval->writefn(eval->fout, "\n", 1);
+ return rv;
+}
+