From fe39ffb8b90ae4e002ed73fe98617cd590abb467 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 08:33:50 +0200 Subject: Adding upstream version 2.4.56. Signed-off-by: Daniel Baumann --- modules/filters/.indent.pro | 54 + modules/filters/Makefile.in | 3 + modules/filters/NWGNUcharsetl | 257 +++ modules/filters/NWGNUdeflate | 279 +++ modules/filters/NWGNUextfiltr | 248 ++ modules/filters/NWGNUmakefile | 273 +++ modules/filters/NWGNUmod_data | 248 ++ modules/filters/NWGNUmod_filter | 248 ++ modules/filters/NWGNUmod_request | 248 ++ modules/filters/NWGNUmodbuffer | 256 ++ modules/filters/NWGNUmodsed | 259 +++ modules/filters/NWGNUproxyhtml | 261 +++ modules/filters/NWGNUratelimit | 256 ++ modules/filters/NWGNUreflector | 256 ++ modules/filters/NWGNUreqtimeout | 256 ++ modules/filters/NWGNUsubstitute | 256 ++ modules/filters/NWGNUxml2enc | 258 +++ modules/filters/config.m4 | 197 ++ modules/filters/libsed.h | 172 ++ modules/filters/mod_brotli.c | 608 +++++ modules/filters/mod_brotli.dep | 45 + modules/filters/mod_brotli.dsp | 111 + modules/filters/mod_brotli.mak | 353 +++ modules/filters/mod_buffer.c | 353 +++ modules/filters/mod_buffer.dep | 48 + modules/filters/mod_buffer.dsp | 111 + modules/filters/mod_buffer.mak | 353 +++ modules/filters/mod_charset_lite.c | 1142 +++++++++ modules/filters/mod_charset_lite.dep | 60 + modules/filters/mod_charset_lite.dsp | 111 + modules/filters/mod_charset_lite.exp | 1 + modules/filters/mod_charset_lite.mak | 353 +++ modules/filters/mod_data.c | 255 ++ modules/filters/mod_data.dep | 55 + modules/filters/mod_data.dsp | 111 + modules/filters/mod_data.mak | 353 +++ modules/filters/mod_deflate.c | 1936 ++++++++++++++++ modules/filters/mod_deflate.dep | 52 + modules/filters/mod_deflate.dsp | 111 + modules/filters/mod_deflate.exp | 1 + modules/filters/mod_deflate.mak | 353 +++ modules/filters/mod_ext_filter.c | 954 ++++++++ modules/filters/mod_ext_filter.dep | 58 + modules/filters/mod_ext_filter.dsp | 111 + modules/filters/mod_ext_filter.exp | 1 + modules/filters/mod_ext_filter.mak | 353 +++ modules/filters/mod_filter.c | 767 ++++++ modules/filters/mod_filter.dep | 50 + modules/filters/mod_filter.dsp | 111 + modules/filters/mod_filter.mak | 353 +++ modules/filters/mod_include.c | 4238 ++++++++++++++++++++++++++++++++++ modules/filters/mod_include.dep | 63 + modules/filters/mod_include.dsp | 115 + modules/filters/mod_include.exp | 1 + modules/filters/mod_include.h | 120 + modules/filters/mod_include.mak | 353 +++ modules/filters/mod_proxy_html.c | 1353 +++++++++++ modules/filters/mod_proxy_html.dep | 58 + modules/filters/mod_proxy_html.dsp | 123 + modules/filters/mod_proxy_html.mak | 352 +++ modules/filters/mod_ratelimit.c | 340 +++ modules/filters/mod_ratelimit.dep | 45 + modules/filters/mod_ratelimit.dsp | 115 + modules/filters/mod_ratelimit.h | 51 + modules/filters/mod_ratelimit.mak | 353 +++ modules/filters/mod_reflector.c | 231 ++ modules/filters/mod_reflector.dep | 57 + modules/filters/mod_reflector.dsp | 111 + modules/filters/mod_reflector.mak | 353 +++ modules/filters/mod_reqtimeout.c | 670 ++++++ modules/filters/mod_reqtimeout.dep | 58 + modules/filters/mod_reqtimeout.dsp | 111 + modules/filters/mod_reqtimeout.mak | 353 +++ modules/filters/mod_request.c | 393 ++++ modules/filters/mod_request.dep | 55 + modules/filters/mod_request.dsp | 115 + modules/filters/mod_request.mak | 353 +++ modules/filters/mod_sed.c | 537 +++++ modules/filters/mod_sed.dep | 109 + modules/filters/mod_sed.dsp | 135 ++ modules/filters/mod_sed.mak | 380 +++ modules/filters/mod_substitute.c | 766 ++++++ modules/filters/mod_substitute.dep | 53 + modules/filters/mod_substitute.dsp | 111 + modules/filters/mod_substitute.mak | 353 +++ modules/filters/mod_xml2enc.c | 676 ++++++ modules/filters/mod_xml2enc.dep | 54 + modules/filters/mod_xml2enc.dsp | 123 + modules/filters/mod_xml2enc.h | 55 + modules/filters/mod_xml2enc.mak | 352 +++ modules/filters/regexp.c | 599 +++++ modules/filters/regexp.h | 112 + modules/filters/sed.h | 61 + modules/filters/sed0.c | 1026 ++++++++ modules/filters/sed1.c | 1110 +++++++++ 95 files changed, 31071 insertions(+) create mode 100644 modules/filters/.indent.pro create mode 100644 modules/filters/Makefile.in create mode 100644 modules/filters/NWGNUcharsetl create mode 100644 modules/filters/NWGNUdeflate create mode 100644 modules/filters/NWGNUextfiltr create mode 100644 modules/filters/NWGNUmakefile create mode 100644 modules/filters/NWGNUmod_data create mode 100644 modules/filters/NWGNUmod_filter create mode 100644 modules/filters/NWGNUmod_request create mode 100644 modules/filters/NWGNUmodbuffer create mode 100644 modules/filters/NWGNUmodsed create mode 100644 modules/filters/NWGNUproxyhtml create mode 100644 modules/filters/NWGNUratelimit create mode 100644 modules/filters/NWGNUreflector create mode 100644 modules/filters/NWGNUreqtimeout create mode 100644 modules/filters/NWGNUsubstitute create mode 100644 modules/filters/NWGNUxml2enc create mode 100644 modules/filters/config.m4 create mode 100644 modules/filters/libsed.h create mode 100644 modules/filters/mod_brotli.c create mode 100644 modules/filters/mod_brotli.dep create mode 100644 modules/filters/mod_brotli.dsp create mode 100644 modules/filters/mod_brotli.mak create mode 100644 modules/filters/mod_buffer.c create mode 100644 modules/filters/mod_buffer.dep create mode 100644 modules/filters/mod_buffer.dsp create mode 100644 modules/filters/mod_buffer.mak create mode 100644 modules/filters/mod_charset_lite.c create mode 100644 modules/filters/mod_charset_lite.dep create mode 100644 modules/filters/mod_charset_lite.dsp create mode 100644 modules/filters/mod_charset_lite.exp create mode 100644 modules/filters/mod_charset_lite.mak create mode 100644 modules/filters/mod_data.c create mode 100644 modules/filters/mod_data.dep create mode 100644 modules/filters/mod_data.dsp create mode 100644 modules/filters/mod_data.mak create mode 100644 modules/filters/mod_deflate.c create mode 100644 modules/filters/mod_deflate.dep create mode 100644 modules/filters/mod_deflate.dsp create mode 100644 modules/filters/mod_deflate.exp create mode 100644 modules/filters/mod_deflate.mak create mode 100644 modules/filters/mod_ext_filter.c create mode 100644 modules/filters/mod_ext_filter.dep create mode 100644 modules/filters/mod_ext_filter.dsp create mode 100644 modules/filters/mod_ext_filter.exp create mode 100644 modules/filters/mod_ext_filter.mak create mode 100644 modules/filters/mod_filter.c create mode 100644 modules/filters/mod_filter.dep create mode 100644 modules/filters/mod_filter.dsp create mode 100644 modules/filters/mod_filter.mak create mode 100644 modules/filters/mod_include.c create mode 100644 modules/filters/mod_include.dep create mode 100644 modules/filters/mod_include.dsp create mode 100644 modules/filters/mod_include.exp create mode 100644 modules/filters/mod_include.h create mode 100644 modules/filters/mod_include.mak create mode 100644 modules/filters/mod_proxy_html.c create mode 100644 modules/filters/mod_proxy_html.dep create mode 100644 modules/filters/mod_proxy_html.dsp create mode 100644 modules/filters/mod_proxy_html.mak create mode 100644 modules/filters/mod_ratelimit.c create mode 100644 modules/filters/mod_ratelimit.dep create mode 100644 modules/filters/mod_ratelimit.dsp create mode 100644 modules/filters/mod_ratelimit.h create mode 100644 modules/filters/mod_ratelimit.mak create mode 100644 modules/filters/mod_reflector.c create mode 100644 modules/filters/mod_reflector.dep create mode 100644 modules/filters/mod_reflector.dsp create mode 100644 modules/filters/mod_reflector.mak create mode 100644 modules/filters/mod_reqtimeout.c create mode 100644 modules/filters/mod_reqtimeout.dep create mode 100644 modules/filters/mod_reqtimeout.dsp create mode 100644 modules/filters/mod_reqtimeout.mak create mode 100644 modules/filters/mod_request.c create mode 100644 modules/filters/mod_request.dep create mode 100644 modules/filters/mod_request.dsp create mode 100644 modules/filters/mod_request.mak create mode 100644 modules/filters/mod_sed.c create mode 100644 modules/filters/mod_sed.dep create mode 100644 modules/filters/mod_sed.dsp create mode 100644 modules/filters/mod_sed.mak create mode 100644 modules/filters/mod_substitute.c create mode 100644 modules/filters/mod_substitute.dep create mode 100644 modules/filters/mod_substitute.dsp create mode 100644 modules/filters/mod_substitute.mak create mode 100644 modules/filters/mod_xml2enc.c create mode 100644 modules/filters/mod_xml2enc.dep create mode 100644 modules/filters/mod_xml2enc.dsp create mode 100644 modules/filters/mod_xml2enc.h create mode 100644 modules/filters/mod_xml2enc.mak create mode 100644 modules/filters/regexp.c create mode 100644 modules/filters/regexp.h create mode 100644 modules/filters/sed.h create mode 100644 modules/filters/sed0.c create mode 100644 modules/filters/sed1.c (limited to 'modules/filters') 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 ], [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 ],[ +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 + +#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 + +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:[;][charset=;]base64, + * + * 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_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, � 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 = ""; + } + } + 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 ? ¤t->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(¤t->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; + } +} + +/* + * + */ +static apr_status_t handle_comment(include_ctx_t *ctx, ap_filter_t *f, + apr_bucket_brigade *bb) +{ + return APR_SUCCESS; +} + +/* + * + * + * 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; +} + +/* + * + */ +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; +} + +/* + * + */ +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; +} + +/* + * + */ +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; +} + +/* + * + */ +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; +} + +/* + * + */ +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; +} + +/* + * + */ +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; +} + +/* + * + */ +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; +} + +/* + * + */ +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; +} + +/* + * + */ +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; +} + +/* + * + */ +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. + */ + 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 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 . + */ + 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 + +#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 = + "\n"; +static const char *const fpi_html_legacy = + "\n"; +static const char *const fpi_xhtml = + "\n"; +static const char *const fpi_xhtml_legacy = + "\n"; +static const char *const fpi_html5 = "\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; if->next, ctx->bb, "&"); break; + case '<' : FLUSH; ap_fputs(ctx->f->next, ctx->bb, "<"); break; + case '>' : FLUSH; ap_fputs(ctx->f->next, ctx->bb, ">"); break; + case '"' : FLUSH; ap_fputs(ctx->f->next, ctx->bb, """); 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, "", 3); + } + else { + 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, "", 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, "]*(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 + +/* 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 + +#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 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 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 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, + "(]*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 +#endif +#if APR_HAVE_STDLIB_H +#include +#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 +#include + +#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; +} + -- cgit v1.2.3