diff options
author | Arno Töll <debian@toell.net> | 2012-01-08 22:53:17 +0100 |
---|---|---|
committer | Arno Töll <debian@toell.net> | 2012-01-08 22:53:17 +0100 |
commit | e072a2dd866b7cb9f14319b80326a4e7fd16fcdf (patch) | |
tree | a49dfc56d94a26011fe157835ff6cbe14edbd8a9 /modules/filters | |
parent | 0890390c00801651d08d3794e13b31a5dabbf5ef (diff) | |
download | apache2-e072a2dd866b7cb9f14319b80326a4e7fd16fcdf.tar.gz |
Imported Upstream version 2.3.16-beta
Diffstat (limited to 'modules/filters')
58 files changed, 11349 insertions, 3560 deletions
diff --git a/modules/filters/NWGNUextfiltr b/modules/filters/NWGNUextfiltr index 16ae72a1..1fd57cb0 100644 --- a/modules/filters/NWGNUextfiltr +++ b/modules/filters/NWGNUextfiltr @@ -94,7 +94,7 @@ endif NLM_NAME = extfiltr # -# This is used by the link '-desc ' directive. +# This is used by the link '-desc ' directive. # If left blank, NLM_NAME will be used. # NLM_DESCRIPTION = Apache $(VERSION_STR) External Filter Module @@ -106,7 +106,7 @@ NLM_DESCRIPTION = Apache $(VERSION_STR) External Filter Module NLM_THREAD_NAME = ExtFilter Module # -# If this is specified, it will override VERSION value in +# If this is specified, it will override VERSION value in # $(AP_WORK)/build/NWGNUenvironment.inc # NLM_VERSION = @@ -138,11 +138,11 @@ NLM_CHECK_SYM = NLM_FLAGS = # -# If this is specified it will be linked in with the XDCData option in the def +# 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 = +XDCDATA = # # If there is an NLM target, put it here @@ -186,7 +186,7 @@ FILES_nlm_modules = \ # 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 # @@ -205,15 +205,15 @@ FILES_nlm_Ximports = \ @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 # @@ -229,7 +229,7 @@ libs :: $(OBJDIR) $(TARGET_lib) nlms :: libs $(TARGET_nlm) # -# Updated this target to create necessary directories and copy files to the +# 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 diff --git a/modules/filters/NWGNUmakefile b/modules/filters/NWGNUmakefile index 2b21b230..922abac9 100644 --- a/modules/filters/NWGNUmakefile +++ b/modules/filters/NWGNUmakefile @@ -154,12 +154,18 @@ XDCDATA = 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 libraries source exists then build the mod_deflate module +# If the zlib library source exists then build the mod_deflate module ifneq "$(ZLIBSDK)" "" ifeq "$(wildcard $(ZLIBSDK))" "$(ZLIBSDK)" TARGET_nlm += $(OBJDIR)/deflate.nlm \ @@ -167,6 +173,16 @@ TARGET_nlm += $(OBJDIR)/deflate.nlm \ 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 # diff --git a/modules/filters/NWGNUmod_data b/modules/filters/NWGNUmod_data new file mode 100644 index 00000000..7f46e9aa --- /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 index 3bc38385..bf323ee7 100644 --- a/modules/filters/NWGNUmod_filter +++ b/modules/filters/NWGNUmod_filter @@ -94,7 +94,7 @@ endif NLM_NAME = mod_filter # -# This is used by the link '-desc ' directive. +# This is used by the link '-desc ' directive. # If left blank, NLM_NAME will be used. # NLM_DESCRIPTION = Apache $(VERSION_STR) Filter Module @@ -106,7 +106,7 @@ NLM_DESCRIPTION = Apache $(VERSION_STR) Filter Module NLM_THREAD_NAME = Filter Module # -# If this is specified, it will override VERSION value in +# If this is specified, it will override VERSION value in # $(AP_WORK)/build/NWGNUenvironment.inc # NLM_VERSION = @@ -138,11 +138,11 @@ NLM_CHECK_SYM = NLM_FLAGS = # -# If this is specified it will be linked in with the XDCData option in the def +# 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 = +XDCDATA = # # If there is an NLM target, put it here @@ -186,7 +186,7 @@ FILES_nlm_modules = \ # 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 # @@ -205,15 +205,15 @@ FILES_nlm_Ximports = \ @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 # @@ -229,7 +229,7 @@ libs :: $(OBJDIR) $(TARGET_lib) nlms :: libs $(TARGET_nlm) # -# Updated this target to create necessary directories and copy files to the +# 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 diff --git a/modules/filters/NWGNUmod_request b/modules/filters/NWGNUmod_request new file mode 100644 index 00000000..21f53cc1 --- /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 00000000..7652a358 --- /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 00000000..158acb9d --- /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 00000000..5337f3b1 --- /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 00000000..f75c8140 --- /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 00000000..67ca26d9 --- /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/NWGNUxml2enc b/modules/filters/NWGNUxml2enc new file mode 100644 index 00000000..117832eb --- /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 index 247d4f65..4ef2d3a5 100644 --- a/modules/filters/config.m4 +++ b/modules/filters/config.m4 @@ -4,18 +4,26 @@ dnl APACHE_MODULE(name, helptext[, objects[, structname[, default[, config]]]]) APACHE_MODPATH_INIT(filters) -APACHE_MODULE(reqtimeout, Limit time waiting for request from client, , , most) +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(include, Server Side Includes, , , yes) +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 "$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, , , yes) + APACHE_MODULE(charset_lite, character set translation. Enabled by default only on EBCDIC systems., , , yes) else - APACHE_MODULE(charset_lite, character set translation, , , no) + APACHE_MODULE(charset_lite, character set translation. Enabled by default only on EBCDIC systems., , , ) fi @@ -24,6 +32,7 @@ APACHE_MODULE(deflate, Deflate transfer encoding support, , , most, [ [ 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 @@ -48,27 +57,82 @@ APACHE_MODULE(deflate, Deflate transfer encoding support, , , most, [ 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]) dnl put in CPPFLAGS temporarily so that AC_TRY_LINK below will work CPPFLAGS="$CPPFLAGS $INCLUDES" APR_ADDTO(LDFLAGS, [-L${ap_zlib_base}/lib]) + APR_ADDTO(ap_zlib_ldflags, [-L${ap_zlib_base}/lib]) if test "x$ap_platform_runtime_link_flag" != "x"; then APR_ADDTO(LDFLAGS, [$ap_platform_runtime_link_flag${ap_zlib_base}/lib]) + APR_ADDTO(ap_zlib_ldflags, [$ap_platform_runtime_link_flag${ap_zlib_base}/lib]) fi fi APR_ADDTO(LIBS, [-lz]) AC_MSG_CHECKING([for zlib library]) AC_TRY_LINK([#include <zlib.h>], [int i = Z_OK;], - [AC_MSG_RESULT(found) - APR_SETVAR(MOD_DEFLATE_LDADD, [-lz])], - [AC_MSG_RESULT(not found) - enable_deflate=no - INCLUDES=$ap_save_includes - LDFLAGS=$ap_save_ldflags]) - APR_REMOVEFROM(LIBS, [-lz]) + [AC_MSG_RESULT(found) + APR_ADDTO(MOD_DEFLATE_LDADD, [$ap_zlib_ldflags -lz])], + [AC_MSG_RESULT(not found) + enable_deflate=no + INCLUDES=$ap_save_includes + 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 + ]) + 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,location for libxml2)], + [test_paths="${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(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(CFLAGS, [-I${XML2_INCLUDES}]) + APR_ADDTO(MOD_PROXY_HTML_LDADD, [-lxml2]) + else + enable_proxy_html=no + 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 00000000..a889d50a --- /dev/null +++ b/modules/filters/libsed.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2005, 2008 Sun Microsystems, Inc. All Rights Reserved. + * Use is subject to license terms. + * + * Copyright (c) 1984 AT&T + * All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBSED_H +#define LIBSED_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <limits.h> + +#include "apr_file_io.h" +#ifndef PATH_MAX +#define PATH_MAX MAX_PATH +#endif + +#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, int 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; + + unsigned 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; + + unsigned lsize; + char *linebuf; + char *lspend; + + unsigned hsize; + char *holdbuf; + char *hspend; + + unsigned 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, int 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_buffer.c b/modules/filters/mod_buffer.c new file mode 100644 index 00000000..cf552aa7 --- /dev/null +++ b/modules/filters/mod_buffer.c @@ -0,0 +1,342 @@ +/* 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)) { + ctx->remaining = ctx->conf->size; + + while (!ctx->seen_eos && ctx->remaining > 0) { + const char *data; + apr_size_t size = 0; + + 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; + } + + for (e = APR_BRIGADE_FIRST(ctx->tmp); e != APR_BRIGADE_SENTINEL( + ctx->tmp); e = APR_BUCKET_NEXT(e)) { + + /* 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); + 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; + } + + } + } + } + + /* 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.dsp b/modules/filters/mod_buffer.dsp new file mode 100644 index 00000000..26834f57 --- /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_charset_lite.c b/modules/filters/mod_charset_lite.c index 76bbe551..25f1ecb2 100644 --- a/modules/filters/mod_charset_lite.c +++ b/modules/filters/mod_charset_lite.c @@ -26,7 +26,7 @@ #include "httpd.h" #include "http_config.h" -#define CORE_PRIVATE + #include "http_core.h" #include "http_log.h" #include "http_main.h" @@ -70,8 +70,6 @@ typedef enum { #define XLATEIN_FILTER_NAME "XLATEIN" typedef struct charset_dir_t { - /** debug level; -1 means uninitialized, 0 means no debug */ - int debug; const char *charset_source; /* source encoding */ const char *charset_default; /* how to ship on wire */ /** module does ap_add_*_filter()? */ @@ -107,19 +105,12 @@ typedef struct charset_req_t { charset_filter_ctx_t *output_ctx, *input_ctx; } charset_req_t; -/* debug level definitions */ -#define DBGLVL_GORY 9 /* gory details */ -#define DBGLVL_FLOW 4 /* enough messages to see what happens on - * each request */ -#define DBGLVL_PMC 2 /* messages about possible misconfiguration */ - 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)); - dc->debug = -1; return dc; } @@ -133,8 +124,6 @@ static void *merge_charset_dir_conf(apr_pool_t *p, void *basev, void *overridesv * from the enclosing container. */ - a->debug = - over->debug != -1 ? over->debug : base->debug; a->charset_default = over->charset_default ? over->charset_default : base->charset_default; a->charset_source = @@ -187,9 +176,6 @@ static const char *add_charset_options(cmd_parms *cmd, void *in_dc, else if (!strcasecmp(flag, "NoTranslateAllMimeTypes")) { dc->force_xlate = FX_NOFORCE; } - else if (!strncasecmp(flag, "DebugLevel=", 11)) { - dc->debug = atoi(flag + 11); - } else { return apr_pstrcat(cmd->temp_pool, "Invalid CharsetOptions option: ", @@ -212,29 +198,25 @@ static int find_code_page(request_rec *r) charset_filter_ctx_t *input_ctx, *output_ctx; apr_status_t rv; - if (dc->debug >= DBGLVL_FLOW) { - ap_log_rerror(APLOG_MARK,APLOG_DEBUG, 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); - } + 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) { - if (dc->debug >= DBGLVL_PMC) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - "incomplete configuration: src %s, dst %s", - dc->charset_source ? dc->charset_source : "unspecified", - dc->charset_default ? dc->charset_default : "unspecified"); - } + 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; } @@ -267,7 +249,7 @@ static int find_code_page(request_rec *r) reqinfo->dc = dc; output_ctx->dc = dc; - output_ctx->tmpbb = apr_brigade_create(r->pool, + output_ctx->tmpbb = apr_brigade_create(r->pool, r->connection->bucket_alloc); ap_set_module_config(r->request_config, &charset_lite_module, reqinfo); @@ -289,7 +271,7 @@ static int find_code_page(request_rec *r) 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, + 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; @@ -337,12 +319,10 @@ static void xlate_insert_filter(request_rec *r) charset_dir_t *dc = ap_get_module_config(r->per_dir_config, &charset_lite_module); - if (dc && (dc->implicit_add == IA_NOIMPADD)) { - if (dc->debug >= DBGLVL_GORY) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - "xlate output filter not added implicitly because " - "CharsetOptions included 'NoImplicitAdd'"); - } + 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; } @@ -351,25 +331,21 @@ static void xlate_insert_filter(request_rec *r) ap_add_output_filter(XLATEOUT_FILTER_NAME, reqinfo->output_ctx, r, r->connection); } - else if (dc->debug >= DBGLVL_FLOW) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - "xlate output filter not added implicitly because %s", - !reqinfo->output_ctx ? - "no output configuration available" : - "another module added the filter"); - } + 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); } - else if (dc->debug >= DBGLVL_FLOW) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - "xlate input filter not added implicitly because %s", - !reqinfo->input_ctx ? - "no input configuration available" : - "another module added the filter"); - } + 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"); } } @@ -503,19 +479,19 @@ static void log_xlate_error(ap_filter_t *f, apr_status_t rv) switch(ctx->ees) { case EES_LIMIT: rv = 0; - msg = "xlate filter - a built-in restriction was encountered"; + msg = APLOGNO(02193) "xlate filter - a built-in restriction was encountered"; break; case EES_BAD_INPUT: rv = 0; - msg = "xlate filter - an input character was invalid"; + msg = APLOGNO(02194) "xlate filter - an input character was invalid"; break; case EES_BUCKET_READ: rv = 0; - msg = "xlate filter - bucket read routine failed"; + msg = APLOGNO(02195) "xlate filter - bucket read routine failed"; break; case EES_INCOMPLETE_CHAR: rv = 0; - strcpy(msgbuf, "xlate filter - incomplete char at end of input - "); + strcpy(msgbuf, APLOGNO(02196) "xlate filter - incomplete char at end of input - "); cur = 0; while ((apr_size_t)cur < ctx->saved) { apr_snprintf(msgbuf + strlen(msgbuf), sizeof(msgbuf) - strlen(msgbuf), @@ -525,13 +501,12 @@ static void log_xlate_error(ap_filter_t *f, apr_status_t rv) msg = msgbuf; break; case EES_DOWNSTREAM: - msg = "xlate filter - an error occurred in a lower filter"; + msg = APLOGNO(02197) "xlate filter - an error occurred in a lower filter"; break; default: - msg = "xlate filter - returning error"; + msg = APLOGNO(02198) "xlate filter - returning error"; } - ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, - "%s", msg); + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, "%s", msg); } /* chk_filter_chain() is called once per filter instance; it tries to @@ -569,7 +544,6 @@ 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 debug = ctx->dc->debug; int output = !strcasecmp(f->frec->name, XLATEOUT_FILTER_NAME); if (ctx->noop) { @@ -603,11 +577,11 @@ static void chk_filter_chain(ap_filter_t *f) */ if (last_xlate_ctx == f->ctx) { last_xlate_ctx->noop = 1; - if (debug >= DBGLVL_PMC) { + if (APLOGrtrace1(f->r)) { const char *symbol = output ? "->" : "<-"; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, - 0, f->r, + 0, f->r, APLOGNO(01451) "%s %s - disabling " "translation %s%s%s; existing " "translation %s%s%s", @@ -625,7 +599,7 @@ static void chk_filter_chain(ap_filter_t *f) const char *symbol = output ? "->" : "<-"; ap_log_rerror(APLOG_MARK, APLOG_ERR, - 0, f->r, + 0, f->r, APLOGNO(01452) "chk_filter_chain() - can't disable " "translation %s%s%s; existing " "translation %s%s%s", @@ -812,9 +786,9 @@ static apr_status_t xlate_out_filter(ap_filter_t *f, apr_bucket_brigade *bb) /* 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 ? f->r->content_type : ap_default_type(f->r); + const char *mime_type = f->r->content_type; - if (strncasecmp(mime_type, "text/", 5) == 0 || + if (mime_type && (strncasecmp(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. @@ -830,13 +804,13 @@ static apr_status_t xlate_out_filter(ap_filter_t *f, apr_bucket_brigade *bb) */ strcmp(mime_type, DIR_MAGIC_TYPE) == 0 || #endif - strncasecmp(mime_type, "message/", 8) == 0 || - dc->force_xlate == FX_FORCE) { + strncasecmp(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, + 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; @@ -849,21 +823,19 @@ static apr_status_t xlate_out_filter(ap_filter_t *f, apr_bucket_brigade *bb) } else { ctx->noop = 1; - if (dc->debug >= DBGLVL_GORY) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, + if (mime_type) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE6, 0, f->r, "mime type is %s; no translation selected", mime_type); } } } - if (dc->debug >= DBGLVL_GORY) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 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)"); - } + 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); @@ -1032,13 +1004,11 @@ static int xlate_in_filter(ap_filter_t *f, apr_bucket_brigade *bb, } } - if (dc->debug >= DBGLVL_GORY) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 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)"); - } + 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); @@ -1054,11 +1024,9 @@ static int xlate_in_filter(ap_filter_t *f, apr_bucket_brigade *bb, * Processing of chunked request bodies is not impacted by this * filter since the the length was not declared anyway. */ - if (dc->debug >= DBGLVL_PMC) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, - "Request body length may change, resulting in " - "misprocessing by some modules or scripts"); - } + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, f->r, + "Request body length may change, resulting in " + "misprocessing by some modules or scripts"); } } @@ -1143,7 +1111,7 @@ static const command_rec cmds[] = NULL, OR_FILEINFO, "valid options: ImplicitAdd, NoImplicitAdd, TranslateAllMimeTypes, " - "NoTranslateAllMimeTypes, DebugLevel=n"), + "NoTranslateAllMimeTypes"), {NULL} }; @@ -1157,7 +1125,7 @@ static void charset_register_hooks(apr_pool_t *p) AP_FTYPE_RESOURCE); } -module AP_MODULE_DECLARE_DATA charset_lite_module = +AP_DECLARE_MODULE(charset_lite) = { STANDARD20_MODULE_STUFF, create_charset_dir_conf, diff --git a/modules/filters/mod_charset_lite.dep b/modules/filters/mod_charset_lite.dep deleted file mode 100644 index e6f20e7d..00000000 --- a/modules/filters/mod_charset_lite.dep +++ /dev/null @@ -1,33 +0,0 @@ -# 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_mmn.h"\ - "..\..\include\ap_regex.h"\ - "..\..\include\ap_release.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_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_hash.h"\ - "..\..\srclib\apr\include\apr_lib.h"\ - "..\..\srclib\apr\include\apr_mmap.h"\ - "..\..\srclib\apr\include\apr_poll.h"\ - "..\..\srclib\apr\include\apr_portable.h"\ - "..\..\srclib\apr\include\apr_strings.h"\ - diff --git a/modules/filters/mod_charset_lite.mak b/modules/filters/mod_charset_lite.mak deleted file mode 100644 index 8870f9ad..00000000 --- a/modules/filters/mod_charset_lite.mak +++ /dev/null @@ -1,353 +0,0 @@ -# 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 00000000..83284dd2 --- /dev/null +++ b/modules/filters/mod_data.c @@ -0,0 +1,254 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * mod_data.c --- Turn the response into an rfc2397 data URL, suitable for + * displaying as inline content on a page. + */ + +#include "apr.h" +#include "apr_strings.h" +#include "apr_buckets.h" +#include "apr_base64.h" +#include "apr_lib.h" + +#include "ap_config.h" +#include "util_filter.h" +#include "httpd.h" +#include "http_config.h" +#include "http_log.h" +#include "http_request.h" +#include "http_protocol.h" + +#define DATA_FILTER "DATA" + +module AP_MODULE_DECLARE_DATA data_module; + +typedef struct data_ctx +{ + unsigned char overflow[3]; + int count; + apr_bucket_brigade *bb; +} data_ctx; + +/** + * Create a data URL as follows: + * + * data:[<mime-type>;][charset=<charset>;]base64,<payload> + * + * Where: + * + * mime-type: The mime type of the original response. + * charset: The optional character set corresponding to the mime type. + * payload: A base64 version of the response body. + * + * The content type of the response is set to text/plain. + * + * The Content-Length header, if present, is updated with the new content + * length based on the increase in size expected from the base64 conversion. + * If the Content-Length header is too large to fit into an int, we remove + * the Content-Length header instead. + */ +static apr_status_t data_out_filter(ap_filter_t *f, apr_bucket_brigade *bb) +{ + apr_bucket *e, *ee; + request_rec *r = f->r; + data_ctx *ctx = f->ctx; + apr_status_t rv = APR_SUCCESS; + + /* first time in? create a context */ + if (!ctx) { + char *type; + char *charset = NULL; + char *end; + const char *content_length; + + /* base64-ing won't work on subrequests, it would be nice if + * it did. Within subrequests, we have no EOS to check for, + * so we don't know when to flush the tail to the network. + */ + if (!ap_is_initial_req(f->r)) { + ap_remove_output_filter(f); + return ap_pass_brigade(f->next, bb); + } + + ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx)); + ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc); + + type = apr_pstrdup(r->pool, r->content_type); + if (type) { + charset = strchr(type, ' '); + if (charset) { + *charset++ = 0; + end = strchr(charset, ' '); + if (end) { + *end++ = 0; + } + } + } + + apr_brigade_printf(ctx->bb, NULL, NULL, "data:%s%s;base64,", + type ? type : "", charset ? charset : ""); + + content_length = apr_table_get(r->headers_out, "Content-Length"); + if (content_length) { + apr_off_t len, clen; + apr_brigade_length(ctx->bb, 1, &len); + clen = apr_atoi64(content_length); + if (clen >= 0 && clen < APR_INT32_MAX) { + ap_set_content_length(r, len + apr_base64_encode_len(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.dsp b/modules/filters/mod_data.dsp new file mode 100644 index 00000000..e7270312 --- /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="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_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="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_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_deflate.c b/modules/filters/mod_deflate.c index 7856e35f..0876cb4e 100644 --- a/modules/filters/mod_deflate.c +++ b/modules/filters/mod_deflate.c @@ -157,6 +157,15 @@ static int check_gzip(request_rec *r, apr_table_t *hdrs1, apr_table_t *hdrs2) } } } + /* + * 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; } @@ -294,6 +303,7 @@ typedef struct deflate_ctx_t unsigned char *validation_buffer; apr_size_t validation_buffer_length; int inflate_init; + int filter_init; } deflate_ctx; /* Number of validation bytes (CRC and length) after the compressed data */ @@ -373,6 +383,42 @@ static apr_status_t deflate_ctx_cleanup(void *data) 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); + } + } +} + static apr_status_t deflate_out_filter(ap_filter_t *f, apr_bucket_brigade *bb) { @@ -380,11 +426,13 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, 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 ap_pass_brigade(f->next, bb); + return APR_SUCCESS; } c = ap_get_module_config(r->server->module_config, @@ -400,22 +448,65 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, char *token; const char *encoding; - /* only work on main request/no subrequests */ - if (r->main != NULL) { - 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; + } - /* some browsers might have problems, so set no-gzip - * (with browsermatch) for them - */ - if (apr_table_get(r->subprocess_env, "no-gzip")) { - ap_remove_output_filter(f); - return ap_pass_brigade(f->next, bb); + rc = apr_bucket_read(e, &data, &blen, APR_BLOCK_READ); + if (rc != APR_SUCCESS) + return rc; + 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); + } } - /* We can't operate on Content-Ranges */ - if (apr_table_get(r->headers_out, "Content-Range") != NULL) { + 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. + */ + 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); } @@ -429,6 +520,8 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, 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); } @@ -465,7 +558,9 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, /* 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); } @@ -501,7 +596,7 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, /* skip parameters, XXX: ;q=foo evaluation? */ while (*accepts == ';') { ++accepts; - token = ap_get_token(r->pool, &accepts, 1); + ap_get_token(r->pool, &accepts, 1); } /* retrieve next token */ @@ -513,53 +608,62 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, /* No acceptable token found. */ if (token == NULL || token[0] == '\0') { + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, + "Not compressing (no Accept-Encoding: gzip)"); ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } } - - /* For a 304 or 204 response there is no entity included in - * the response and hence nothing to deflate. */ - if (r->status == HTTP_NOT_MODIFIED || r->status == HTTP_NO_CONTENT) { - 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)"); } - /* We're cool with filtering this. */ - ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx)); - ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc); - ctx->buffer = apr_palloc(r->pool, c->bufferSize); - ctx->libz_end_func = deflateEnd; + /* 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). + */ - zRC = deflateInit2(&ctx->stream, c->compressionlevel, Z_DEFLATED, - c->windowSize, c->memlevel, - Z_DEFAULT_STRATEGY); + 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; - if (zRC != Z_OK) { - deflateEnd(&ctx->stream); - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "unable to init Zlib: " - "deflateInit2 returned %d: URL %s", - zRC, r->uri); + 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); + } /* - * Remove ourselves as it does not make sense to return: - * We are not able to init libz and pass data down the chain - * uncompressed. + * Register a cleanup function to ensure that we cleanup the internal + * libz resources. */ - ap_remove_output_filter(f); - return ap_pass_brigade(f->next, bb); + 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; } + /* - * Register a cleanup function to ensure that we cleanup the internal - * libz resources. + * Zlib initialization worked, so we can now change the important + * content metadata before sending the response out. */ - apr_pool_cleanup_register(r->pool, ctx, deflate_ctx_cleanup, - apr_pool_cleanup_null); - - /* 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); /* If the entire Content-Encoding is "identity", we can replace it. */ if (!encoding || !strcasecmp(encoding, "identity")) { @@ -568,19 +672,39 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, 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)) { - const char *data; apr_bucket *b; - apr_size_t len; /* * Optimization: If we are a HEAD request and bytes_sent is not zero @@ -612,7 +736,7 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, 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, + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01384) "Zlib: Compressed %ld to %ld : URL %s", ctx->stream.total_in, ctx->stream.total_out, r->uri); @@ -664,6 +788,9 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, 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; } @@ -720,6 +847,9 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, 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; } } @@ -803,11 +933,14 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, if (len != 10 || deflate_hdr[0] != deflate_magic[0] || deflate_hdr[1] != deflate_magic[1]) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01387) "Zlib: Invalid header"); return APR_EGENERAL; } /* We can't handle flags for now. */ if (deflate_hdr[3] != 0) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01388) + "Zlib: Unsupported flags %02x", (int)deflate_hdr[3]); return APR_EGENERAL; } @@ -816,7 +949,7 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, if (zRC != Z_OK) { f->ctx = NULL; inflateEnd(&ctx->stream); - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01389) "unable to init Zlib: " "inflateInit2 returned %d: URL %s", zRC, r->uri); @@ -850,6 +983,8 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, /* If we actually see the EOS, that means we screwed up! */ if (APR_BUCKET_IS_EOS(bkt)) { inflateEnd(&ctx->stream); + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01390) + "Encountered EOS bucket in inflate filter (bug?)"); return APR_EGENERAL; } @@ -858,6 +993,9 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH); 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; } @@ -906,13 +1044,16 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, if (zRC != Z_OK) { 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 (zRC == Z_STREAM_END) { apr_bucket *tmp_heap, *eos; - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01393) "Zlib: Inflated %ld to %ld : URL %s", ctx->stream.total_in, ctx->stream.total_out, r->uri); @@ -931,12 +1072,18 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, compCRC = getLong(ctx->stream.next_in); 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; } ctx->stream.next_in += 4; compLen = getLong(ctx->stream.next_in); if (ctx->stream.total_out != compLen) { inflateEnd(&ctx->stream); + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01395) + "Zlib: Length %ld of inflated data does " + "not match expected value %ld", + ctx->stream.total_out, compLen); return APR_EGENERAL; } } @@ -944,6 +1091,8 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, /* FIXME: We need to grab the 8 verification bytes * from the wire! */ inflateEnd(&ctx->stream); + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01396) + "Verification data not available (bug?)"); return APR_EGENERAL; } @@ -977,14 +1126,13 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, } if (!APR_BRIGADE_EMPTY(ctx->proc_bb)) { - apr_bucket_brigade *newbb; - - /* May return APR_INCOMPLETE which is fine by us. */ - apr_brigade_partition(ctx->proc_bb, readbytes, &bkt); - - newbb = apr_brigade_split(ctx->proc_bb, bkt); - APR_BRIGADE_CONCAT(bb, ctx->proc_bb); - APR_BRIGADE_CONCAT(ctx->proc_bb, newbb); + 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); + } } return APR_SUCCESS; @@ -1006,36 +1154,38 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, /* Do nothing if asked to filter nothing. */ if (APR_BRIGADE_EMPTY(bb)) { - return ap_pass_brigade(f->next, bb); + return APR_SUCCESS; } c = ap_get_module_config(r->server->module_config, &deflate_module); if (!ctx) { - /* only work on main request/no subrequests */ - if (!ap_is_initial_req(r)) { - ap_remove_output_filter(f); - return ap_pass_brigade(f->next, bb); - } - - /* We can't operate on Content-Ranges */ - if (apr_table_get(r->headers_out, "Content-Range") != NULL) { + /* + * 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. + */ + 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); } /* - * Let's see what our current Content-Encoding is. - * Only inflate if gzipped. + * 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. */ - if (check_gzip(r, r->headers_out, r->err_headers_out) == 0) { - ap_remove_output_filter(f); - return ap_pass_brigade(f->next, bb); - } + apr_table_unset(r->headers_out, "Content-Length"); + apr_table_unset(r->headers_out, "Content-MD5"); + deflate_check_etag(r, "gunzip"); - /* No need to inflate HEAD or 204/304 */ - if (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(bb))) { + /* 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); } @@ -1052,7 +1202,7 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, if (zRC != Z_OK) { f->ctx = NULL; inflateEnd(&ctx->stream); - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01397) "unable to init Zlib: " "inflateInit2 returned %d: URL %s", zRC, r->uri); @@ -1072,10 +1222,6 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, apr_pool_cleanup_register(r->pool, ctx, deflate_ctx_cleanup, apr_pool_cleanup_null); - /* these are unlikely to be set anyway, but ... */ - apr_table_unset(r->headers_out, "Content-Length"); - apr_table_unset(r->headers_out, "Content-MD5"); - /* initialize inflate output buffer */ ctx->stream.next_out = ctx->buffer; ctx->stream.avail_out = c->bufferSize; @@ -1110,7 +1256,7 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, */ flush_libz_buffer(ctx, c, f->c->bucket_alloc, inflate, Z_SYNC_FLUSH, UPDATE_CRC); - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01398) "Zlib: Inflated %ld to %ld : URL %s", ctx->stream.total_in, ctx->stream.total_out, r->uri); @@ -1118,20 +1264,20 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, unsigned long compCRC, compLen; compCRC = getLong(ctx->validation_buffer); if (ctx->crc != compCRC) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + 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); if (ctx->stream.total_out != compLen) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + 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, + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01401) "Zlib: Validation bytes not present"); return APR_EGENERAL; } @@ -1158,6 +1304,9 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, zRC = flush_libz_buffer(ctx, c, f->c->bucket_alloc, inflate, Z_SYNC_FLUSH, UPDATE_CRC); 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; } @@ -1187,7 +1336,7 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, /* first bucket contains zlib header */ if (!ctx->inflate_init++) { if (len < 10) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01403) "Insufficient data for inflate"); return APR_EGENERAL; } @@ -1195,7 +1344,7 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, zlib_method = data[2]; zlib_flags = data[3]; if (zlib_method != Z_DEFLATED) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + 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); @@ -1203,7 +1352,7 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, if (data[0] != deflate_magic[0] || data[1] != deflate_magic[1] || (zlib_flags & RESERVED) != 0) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01405) "inflate: bad header"); return APR_EGENERAL ; } @@ -1215,7 +1364,7 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, bytes += ((unsigned int)(data[1])) << 8; bytes += 2; if (len < bytes) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01406) "inflate: extra field too big (not " "supported)"); return APR_EGENERAL; @@ -1253,7 +1402,7 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, ctx->validation_buffer_length += copy_size; } if (ctx->stream.avail_in) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + 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); /* @@ -1267,8 +1416,6 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, } } - zRC = Z_OK; - while (ctx->stream.avail_in != 0) { if (ctx->stream.avail_out == 0) { @@ -1299,7 +1446,7 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, VALIDATION_SIZE); if (ctx->stream.avail_in > VALIDATION_SIZE) { ctx->validation_buffer_length = VALIDATION_SIZE; - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + 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); @@ -1313,6 +1460,9 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, } if (zRC != Z_OK) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01409) + "Zlib error %d inflating data (%s)", zRC, + ctx->stream.msg); return APR_EGENERAL; } } @@ -1349,7 +1499,7 @@ static const command_rec deflate_filter_cmds[] = { {NULL} }; -module AP_MODULE_DECLARE_DATA deflate_module = { +AP_DECLARE_MODULE(deflate) = { STANDARD20_MODULE_STUFF, NULL, /* dir config creater */ NULL, /* dir merger --- default is to override */ diff --git a/modules/filters/mod_deflate.dep b/modules/filters/mod_deflate.dep deleted file mode 100644 index 75fc104e..00000000 --- a/modules/filters/mod_deflate.dep +++ /dev/null @@ -1,28 +0,0 @@ -# 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_mmn.h"\ - "..\..\include\ap_regex.h"\ - "..\..\include\ap_release.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_hooks.h"\ - "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ - "..\..\srclib\apr-util\include\apr_uri.h"\ - "..\..\srclib\apr\include\apr_lib.h"\ - "..\..\srclib\apr\include\apr_mmap.h"\ - "..\..\srclib\apr\include\apr_poll.h"\ - "..\..\srclib\apr\include\apr_strings.h"\ - "..\..\srclib\zlib\zconf.h"\ - "..\..\srclib\zlib\zlib.h"\ - diff --git a/modules/filters/mod_deflate.mak b/modules/filters/mod_deflate.mak deleted file mode 100644 index 4549eabb..00000000 --- a/modules/filters/mod_deflate.mak +++ /dev/null @@ -1,353 +0,0 @@ -# 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 "../../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 "../../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 index fe922716..3412c21d 100644 --- a/modules/filters/mod_ext_filter.c +++ b/modules/filters/mod_ext_filter.c @@ -22,7 +22,7 @@ #include "http_config.h" #include "http_log.h" #include "http_protocol.h" -#define CORE_PRIVATE + #include "http_core.h" #include "apr_buckets.h" #include "util_filter.h" @@ -56,7 +56,6 @@ typedef struct ef_filter_t { } ef_filter_t; typedef struct ef_dir_t { - int debug; int log_stderr; int onfail; } ef_dir_t; @@ -81,16 +80,12 @@ 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 DBGLVL_SHOWOPTIONS 1 -#define DBGLVL_GORY 9 - #define ERRFN_USERDATA_KEY "EXTFILTCHILDERRFN" static void *create_ef_dir_conf(apr_pool_t *p, char *dummy) { ef_dir_t *dc = (ef_dir_t *)apr_pcalloc(p, sizeof(ef_dir_t)); - dc->debug = -1; dc->log_stderr = -1; dc->onfail = -1; @@ -112,13 +107,6 @@ static void *merge_ef_dir_conf(apr_pool_t *p, void *basev, void *overridesv) ef_dir_t *a = (ef_dir_t *)apr_pcalloc (p, sizeof(ef_dir_t)); ef_dir_t *base = (ef_dir_t *)basev, *over = (ef_dir_t *)overridesv; - if (over->debug != -1) { /* if admin coded something... */ - a->debug = over->debug; - } - else { - a->debug = base->debug; - } - if (over->log_stderr != -1) { /* if admin coded something... */ a->log_stderr = over->log_stderr; } @@ -141,10 +129,7 @@ static const char *add_options(cmd_parms *cmd, void *in_dc, { ef_dir_t *dc = in_dc; - if (!strncasecmp(arg, "DebugLevel=", 11)) { - dc->debug = atoi(arg + 11); - } - else if (!strcasecmp(arg, "LogStderr")) { + if (!strcasecmp(arg, "LogStderr")) { dc->log_stderr = 1; } else if (!strcasecmp(arg, "NoLogStderr")) { @@ -354,7 +339,7 @@ static const command_rec cmds[] = add_options, NULL, ACCESS_CONF, /* same as SetInputFilter/SetOutputFilter */ - "valid options: DebugLevel=n, LogStderr, NoLogStderr"), + "valid options: LogStderr, NoLogStderr"), AP_INIT_RAW_ARGS("ExtFilterDefine", define_filter, NULL, @@ -380,8 +365,7 @@ static apr_status_t set_resource_limits(request_rec *r, #if defined(RLIMIT_CPU) || defined(RLIMIT_NPROC) || \ defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS) core_dir_config *conf = - (core_dir_config *)ap_get_module_config(r->per_dir_config, - &core_module); + (core_dir_config *)ap_get_core_module_config(r->per_dir_config); apr_status_t rv; #ifdef RLIMIT_CPU @@ -422,7 +406,7 @@ static void child_errfn(apr_pool_t *pool, apr_status_t err, const char *descript apr_file_printf(stderr_log, "[%s] [client %s] mod_ext_filter (%d)%s: %s\n", time_str, - r->connection->remote_ip, + r->useragent_ip, err, apr_strerror(err, errbuf, sizeof(errbuf)), description); @@ -493,7 +477,7 @@ static apr_status_t init_ext_filter_process(ap_filter_t *f) ctx->procattr, ctx->p); if (rc != APR_SUCCESS) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, f->r, + 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; @@ -537,8 +521,6 @@ static apr_status_t init_ext_filter_process(ap_filter_t *f) static const char *get_cfg_string(ef_dir_t *dc, ef_filter_t *filter, apr_pool_t *p) { - const char *debug_str = dc->debug == -1 ? - "DebugLevel=0" : apr_psprintf(p, "DebugLevel=%d", dc->debug); const char *log_stderr_str = dc->log_stderr < 1 ? "NoLogStderr" : "LogStderr"; const char *preserve_content_length_str = filter->preserves_content_length ? @@ -549,9 +531,9 @@ static const char *get_cfg_string(ef_dir_t *dc, ef_filter_t *filter, apr_pool_t "(unchanged)" : filter->outtype; return apr_psprintf(p, - "ExtFilterOptions %s %s %s ExtFilterInType %s " + "ExtFilterOptions %s %s ExtFilterInType %s " "ExtFilterOuttype %s", - debug_str, log_stderr_str, preserve_content_length_str, + log_stderr_str, preserve_content_length_str, intype_str, outtype_str); } @@ -583,7 +565,7 @@ static apr_status_t init_filter_instance(ap_filter_t *f) /* 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, + 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; @@ -638,8 +620,8 @@ static apr_status_t init_filter_instance(ap_filter_t *f) } } - if (dc->debug >= DBGLVL_SHOWOPTIONS) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, + 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, @@ -662,23 +644,20 @@ static apr_status_t drain_available_output(ap_filter_t *f, request_rec *r = f->r; conn_rec *c = r->connection; ef_ctx_t *ctx = f->ctx; - ef_dir_t *dc = ctx->dc; apr_size_t len; char buf[4096]; apr_status_t rv; apr_bucket *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)) || - dc->debug >= DBGLVL_GORY) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, - "apr_file_read(child output), len %" APR_SIZE_T_FMT, - !rv ? len : -1); - } + 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, + !rv ? len : -1); if (rv != APR_SUCCESS) { return rv; } @@ -696,7 +675,6 @@ 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; - ef_dir_t *dc = ctx->dc; apr_status_t rv; apr_size_t bytes_written = 0; apr_size_t tmplen; @@ -708,7 +686,7 @@ static apr_status_t pass_data_to_filter(ap_filter_t *f, const char *data, &tmplen); bytes_written += tmplen; if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, + 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; @@ -726,23 +704,20 @@ static apr_status_t pass_data_to_filter(ap_filter_t *f, const char *data, rv = apr_pollset_poll(ctx->pollset, f->r->server->timeout, &num_events, &pdesc); - if (rv || dc->debug >= DBGLVL_GORY) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, - rv, f->r, "apr_pollset_poll()"); - } 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(100000); /* 100 milliseconds */ - if (dc->debug >= DBGLVL_GORY) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, - 0, f->r, "apr_sleep()"); - } + ap_log_rerror(APLOG_MARK, APLOG_TRACE6, 0, f->r, "apr_sleep()"); #endif /* APR_FILES_AS_SOCKETS */ } else if (rv != APR_SUCCESS) { @@ -765,7 +740,6 @@ static int ef_unified_filter(ap_filter_t *f, apr_bucket_brigade *bb) conn_rec *c = r->connection; ef_ctx_t *ctx = f->ctx; apr_bucket *b; - ef_dir_t *dc; apr_size_t len; const char *data; apr_status_t rv; @@ -773,7 +747,6 @@ static int ef_unified_filter(ap_filter_t *f, apr_bucket_brigade *bb) apr_bucket *eos = NULL; apr_bucket_brigade *bb_tmp; - dc = ctx->dc; bb_tmp = apr_brigade_create(r->pool, c->bucket_alloc); for (b = APR_BRIGADE_FIRST(bb); @@ -787,7 +760,7 @@ static int ef_unified_filter(ap_filter_t *f, apr_bucket_brigade *bb) rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ); if (rv != APR_SUCCESS) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "apr_bucket_read()"); + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01463) "apr_bucket_read()"); return rv; } @@ -808,7 +781,7 @@ static int ef_unified_filter(ap_filter_t *f, apr_bucket_brigade *bb) * 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, + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01464) "apr_file_close(child input)"); return rv; } @@ -818,23 +791,21 @@ static int ef_unified_filter(ap_filter_t *f, apr_bucket_brigade *bb) rv = apr_file_pipe_timeout_set(ctx->proc->out, r->server->timeout); if (rv) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + 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)) || - dc->debug >= DBGLVL_GORY) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, - "apr_file_read(child output), len %" APR_SIZE_T_FMT, - !rv ? len : -1); - } + 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, + !rv ? len : -1); if (APR_STATUS_IS_EAGAIN(rv)) { if (eos) { /* should not occur, because we have an APR timeout in place */ @@ -870,7 +841,7 @@ static apr_status_t ef_output_filter(ap_filter_t *f, apr_bucket_brigade *bb) if (!ctx) { if ((rv = init_filter_instance(f)) != APR_SUCCESS) { ctx = f->ctx; - ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + 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"); @@ -902,12 +873,12 @@ static apr_status_t ef_output_filter(ap_filter_t *f, apr_bucket_brigade *bb) rv = ef_unified_filter(f, bb); if (rv != APR_SUCCESS) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + 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_ERR, rv, r, + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(01469) "ap_pass_brigade() failed"); } return rv; @@ -923,7 +894,7 @@ static int ef_input_filter(ap_filter_t *f, apr_bucket_brigade *bb, if (!ctx) { if ((rv = init_filter_instance(f)) != APR_SUCCESS) { ctx = f->ctx; - ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, + 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"); @@ -953,7 +924,7 @@ static int ef_input_filter(ap_filter_t *f, apr_bucket_brigade *bb, return rv; } -module AP_MODULE_DECLARE_DATA ext_filter_module = +AP_DECLARE_MODULE(ext_filter) = { STANDARD20_MODULE_STUFF, create_ef_dir_conf, diff --git a/modules/filters/mod_ext_filter.dep b/modules/filters/mod_ext_filter.dep deleted file mode 100644 index 29ef590c..00000000 --- a/modules/filters/mod_ext_filter.dep +++ /dev/null @@ -1,32 +0,0 @@ -# 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_mmn.h"\ - "..\..\include\ap_regex.h"\ - "..\..\include\ap_release.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_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_hash.h"\ - "..\..\srclib\apr\include\apr_lib.h"\ - "..\..\srclib\apr\include\apr_mmap.h"\ - "..\..\srclib\apr\include\apr_poll.h"\ - "..\..\srclib\apr\include\apr_portable.h"\ - "..\..\srclib\apr\include\apr_strings.h"\ - diff --git a/modules/filters/mod_ext_filter.mak b/modules/filters/mod_ext_filter.mak deleted file mode 100644 index 08faf1d2..00000000 --- a/modules/filters/mod_ext_filter.mak +++ /dev/null @@ -1,353 +0,0 @@ -# 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 index ad7aba3d..32d8bd33 100644 --- a/modules/filters/mod_filter.c +++ b/modules/filters/mod_filter.c @@ -24,6 +24,7 @@ #include "http_request.h" #include "http_log.h" #include "util_filter.h" +#include "ap_expr.h" module AP_MODULE_DECLARE_DATA filter_module; @@ -35,46 +36,14 @@ module AP_MODULE_DECLARE_DATA filter_module; * (2.0-compatible) ap_filter_rec_t* frec. */ struct ap_filter_provider_t { - /** How to match this provider to filter dispatch criterion */ - enum { - STRING_MATCH, - STRING_CONTAINS, - REGEX_MATCH, - INT_EQ, - INT_LT, - INT_LE, - INT_GT, - INT_GE, - DEFINED - } match_type; - - /** negation on match_type */ - int not; - - /** The dispatch match itself - union member depends on match_type */ - union { - const char *string; - ap_regex_t *regex; - int number; - } match; + 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; - - /** Dispatch criteria for filter providers */ - enum { - HANDLER, - REQUEST_HEADERS, - RESPONSE_HEADERS, - SUBPROCESS_ENV, - CONTENT_TYPE - } dispatch; - - /** Match value for filter providers */ - const char* value; }; /** we need provider_ctx to save ctx values set by providers in filter_init */ @@ -114,12 +83,12 @@ static void filter_trace(conn_rec *c, int debug, const char *fname, case 0: /* normal, operational use */ return; case 1: /* mod_diagnostics level */ - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "%s", fname); + 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, + 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); @@ -138,14 +107,14 @@ static int filter_init(ap_filter_t *f) 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, + 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, + 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 */ } @@ -162,109 +131,55 @@ static int filter_init(ap_filter_t *f) f->ctx = fctx; return OK; } - static int filter_lookup(ap_filter_t *f, ap_filter_rec_t *filter) { ap_filter_provider_t *provider; - const char *str = NULL; - char *str1; - int match; - unsigned int proto_flags; + 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) { - match = 1; - switch (provider->dispatch) { - case REQUEST_HEADERS: - str = apr_table_get(r->headers_in, provider->value); - break; - case RESPONSE_HEADERS: - /* Try r->headers_out first, fall back on err_headers_out. */ - str = apr_table_get(r->headers_out, provider->value); - if (str) { - break; + 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; } - str = apr_table_get(r->err_headers_out, provider->value); - break; - case SUBPROCESS_ENV: - str = apr_table_get(r->subprocess_env, provider->value); - break; - case CONTENT_TYPE: - str = r->content_type; - break; - case HANDLER: - str = r->handler; - break; - } - - /* treat nulls so we don't have to check every strcmp individually - * Not sure if there's anything better to do with them - */ - if (!str) { - match = 0; - } - /* we can't check for NULL in provider as that kills integer 0 - * so we have to test each string/regexp case in the switch - */ - else { - switch (provider->match_type) { - case STRING_MATCH: - if (strcasecmp(str, provider->match.string)) { - match = 0; - } - break; - case STRING_CONTAINS: - str1 = apr_pstrdup(r->pool, str); - ap_str_tolower(str1); - if (!strstr(str1, provider->match.string)) { - match = 0; - } - break; - case REGEX_MATCH: - if (ap_regexec(provider->match.regex, str, 0, NULL, 0) - == AP_REG_NOMATCH) { - match = 0; - } - break; - case INT_EQ: - if (atoi(str) != provider->match.number) { - match = 0; - } - break; - /* Integer comparisons should be [var] OP [match] - * We need to set match = 0 if the condition fails - */ - case INT_LT: - if (atoi(str) >= provider->match.number) { - match = 0; - } - break; - case INT_LE: - if (atoi(str) > provider->match.number) { - 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); + while (*type) { + /* Handle 'content-type;charset=...' correctly */ + if (strncmp(*type, r->content_type, len) == 0 + && (*type)[len] == '\0') { + match = 1; + break; } - break; - case INT_GT: - if (atoi(str) <= provider->match.number) { - match = 0; - } - break; - case INT_GE: - if (atoi(str) < provider->match.number) { - match = 0; - } - break; - case DEFINED: /* we already handled this:-) */ - break; + type++; } + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "Content-Type condition for '%s' %s", + provider->frec->name, + match ? "matched" : "did not match"); } - if (match != provider->not) { + if (match) { /* condition matches this provider */ #ifndef NO_PROTOCOL /* check protocol @@ -289,9 +204,10 @@ static int filter_lookup(ap_filter_t *f, ap_filter_rec_t *filter) } if (proto_flags & AP_FILTER_PROTO_TRANSFORM) { - str = apr_table_get(r->headers_out, "Cache-Control"); + const char *str = apr_table_get(r->headers_out, + "Cache-Control"); if (str) { - str1 = apr_pstrdup(r->pool, str); + char *str1 = apr_pstrdup(r->pool, str); ap_str_tolower(str1); if (strstr(str1, "no-transform")) { /* can't use this provider; try next */ @@ -346,8 +262,10 @@ static int filter_lookup(ap_filter_t *f, ap_filter_rec_t *filter) static apr_status_t filter_harness(ap_filter_t *f, apr_bucket_brigade *bb) { apr_status_t ret; +#ifndef NO_PROTOCOL const char *cachecontrol; char *str; +#endif harness_ctx *ctx = f->ctx; ap_filter_rec_t *filter = f->frec; @@ -501,27 +419,17 @@ static const char *filter_declare(cmd_parms *cmd, void *CFG, const char *fname, return NULL; } -static const char *filter_provider(cmd_parms *cmd, void *CFG, const char *args) +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; - int flags; ap_filter_provider_t *provider; - const char *rxend; const char *c; - char *str; - const char *eq; ap_filter_rec_t* frec; ap_filter_rec_t* provider_frec; - - /* insist on exactly four arguments */ - const char *fname = ap_getword_conf(cmd->pool, &args) ; - const char *pname = ap_getword_conf(cmd->pool, &args) ; - const char *condition = ap_getword_conf(cmd->pool, &args) ; - const char *match = ap_getword_conf(cmd->pool, &args) ; - eq = ap_getword_conf(cmd->pool, &args) ; - if ( !*fname || !*pname || !*match || !*condition || *eq ) { - return "usage: FilterProvider filter provider condition match" ; - } + ap_expr_info_t *node; + const char *err = NULL; /* fname has been declared with DeclareFilter, so we can look it up */ frec = apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING); @@ -544,118 +452,32 @@ static const char *filter_provider(cmd_parms *cmd, void *CFG, const char *args) if (!provider_frec) { return apr_psprintf(cmd->pool, "Unknown filter provider %s", pname); } - provider = apr_palloc(cmd->pool, sizeof(ap_filter_provider_t)); - if (*match == '!') { - provider->not = 1; - ++match; + 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; } else { - provider->not = 0; - } - - switch (*match++) { - case '<': - if (*match == '=') { - provider->match_type = INT_LE; - ++match; - } - else { - provider->match_type = INT_LT; - } - provider->match.number = atoi(match); - break; - case '>': - if (*match == '=') { - provider->match_type = INT_GE; - ++match; - } - else { - provider->match_type = INT_GT; - } - provider->match.number = atoi(match); - break; - case '=': - provider->match_type = INT_EQ; - provider->match.number = atoi(match); - break; - case '/': - provider->match_type = REGEX_MATCH; - rxend = ap_strrchr_c(match, '/'); - if (!rxend) { - return "Bad regexp syntax"; - } - flags = AP_REG_NOSUB; /* we're not mod_rewrite:-) */ - for (c = rxend+1; *c; ++c) { - switch (*c) { - case 'i': flags |= AP_REG_ICASE; break; - } - } - provider->match.regex = ap_pregcomp(cmd->pool, - apr_pstrndup(cmd->pool, - match, - rxend-match), - flags); - if (provider->match.regex == NULL) { - return "Bad regexp"; - } - break; - case '*': - provider->match_type = DEFINED; - provider->match.number = -1; - break; - case '$': - provider->match_type = STRING_CONTAINS; - str = apr_pstrdup(cmd->pool, match); - ap_str_tolower(str); - provider->match.string = str; - break; - default: - provider->match_type = STRING_MATCH; - provider->match.string = apr_pstrdup(cmd->pool, match-1); - break; + provider->types = types; } provider->frec = provider_frec; provider->next = frec->providers; frec->providers = provider; - - /* determine what a filter will dispatch this provider on */ - eq = ap_strchr_c(condition, '='); - if (eq) { - str = apr_pstrdup(cmd->pool, eq+1); - if (!strncasecmp(condition, "env=", 4)) { - provider->dispatch = SUBPROCESS_ENV; - } - else if (!strncasecmp(condition, "req=", 4)) { - provider->dispatch = REQUEST_HEADERS; - } - else if (!strncasecmp(condition, "resp=", 5)) { - provider->dispatch = RESPONSE_HEADERS; - } - else { - return "FilterProvider: unrecognized dispatch table"; - } - } - else { - if (!strcasecmp(condition, "handler")) { - provider->dispatch = HANDLER; - } - else { - provider->dispatch = RESPONSE_HEADERS; - } - str = apr_pstrdup(cmd->pool, condition); - ap_str_tolower(str); - } - - if ( (provider->dispatch == RESPONSE_HEADERS) - && !strcasecmp(str, "content-type")) { - provider->dispatch = CONTENT_TYPE; - } - provider->value = str; - 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; @@ -698,8 +520,8 @@ static const char *filter_chain(cmd_parms *cmd, void *CFG, const char *arg) break; case '!': /* Empty the chain */ - /** IG: Add a NULL provider to the beginning so that - * we can ensure that we'll empty everything before + /** 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; @@ -731,6 +553,56 @@ static const char *filter_chain(cmd_parms *cmd, void *CFG, const char *arg) 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] = 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) { @@ -761,14 +633,14 @@ static void filter_insert(request_rec *r) * through the chain, and prune out the NULL filters */ for (p = cfg->chain; p; p = p->next) { - if (p->fname == NULL) + 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, + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01380) "Unknown filter %s not added", p->fname); continue; } @@ -853,14 +725,15 @@ static void *filter_merge(apr_pool_t *pool, void *BASE, void *ADD) static const command_rec filter_cmds[] = { AP_INIT_TAKE12("FilterDeclare", filter_declare, NULL, OR_OPTIONS, - "filter-name [, filter-type]"), - /** we don't have a TAKE4, so we have to use RAW_ARGS */ - AP_INIT_RAW_ARGS("FilterProvider", filter_provider, NULL, OR_OPTIONS, - "filter-name, provider-name, dispatch--criterion, dispatch-match"), + "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, - "Debug level"), + "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"), @@ -868,7 +741,7 @@ static const command_rec filter_cmds[] = { { NULL } }; -module AP_MODULE_DECLARE_DATA filter_module = { +AP_DECLARE_MODULE(filter) = { STANDARD20_MODULE_STUFF, filter_config, filter_merge, diff --git a/modules/filters/mod_filter.dep b/modules/filters/mod_filter.dep deleted file mode 100644 index dda01b4e..00000000 --- a/modules/filters/mod_filter.dep +++ /dev/null @@ -1,27 +0,0 @@ -# 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_mmn.h"\ - "..\..\include\ap_regex.h"\ - "..\..\include\ap_release.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_hooks.h"\ - "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ - "..\..\srclib\apr-util\include\apr_uri.h"\ - "..\..\srclib\apr\include\apr_hash.h"\ - "..\..\srclib\apr\include\apr_lib.h"\ - "..\..\srclib\apr\include\apr_mmap.h"\ - "..\..\srclib\apr\include\apr_poll.h"\ - "..\..\srclib\apr\include\apr_strings.h"\ - diff --git a/modules/filters/mod_filter.mak b/modules/filters/mod_filter.mak deleted file mode 100644 index c1728072..00000000 --- a/modules/filters/mod_filter.mak +++ /dev/null @@ -1,353 +0,0 @@ -# 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 index 59ce8e3e..ffca2328 100644 --- a/modules/filters/mod_include.c +++ b/modules/filters/mod_include.c @@ -39,6 +39,7 @@ #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 @@ -107,7 +108,8 @@ typedef struct parse_node { typedef enum { XBITHACK_OFF, XBITHACK_ON, - XBITHACK_FULL + XBITHACK_FULL, + XBITHACK_UNSET } xbithack_t; typedef struct { @@ -115,9 +117,9 @@ typedef struct { const char *default_time_fmt; const char *undefined_echo; xbithack_t xbithack; - int accessenable; - int lastmodified; - int etag; + signed char lastmodified; + signed char etag; + signed char legacy_expr; } include_dir_config; typedef struct { @@ -160,7 +162,7 @@ typedef struct { const char *rexp; apr_size_t nsub; ap_regmatch_t match[AP_MAX_REG_MATCH]; - int have_match; + int have_match; } backref_t; typedef struct { @@ -179,7 +181,6 @@ struct ssi_internal_ctx { apr_bucket_brigade *tmp_bb; - request_rec *r; const char *start_seq; bndm_t *start_seq_pat; const char *end_seq; @@ -195,8 +196,12 @@ struct ssi_internal_ctx { const char *undefined_echo; apr_size_t undefined_echo_len; - int accessenable; /* is using the access tests allowed? */ + 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; @@ -454,7 +459,7 @@ 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; +static const char lazy_eval_sentinel = '\0'; #define LAZY_VALUE (&lazy_eval_sentinel) /* default values */ @@ -463,9 +468,8 @@ static const char lazy_eval_sentinel; #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 DEFAULT_ACCESSENABLE 0 -#define DEFAULT_LASTMODIFIED 0 -#define DEFAULT_ETAG 0 + +#define UNSET -1 #ifdef XBITHACK #define DEFAULT_XBITHACK XBITHACK_FULL @@ -644,7 +648,7 @@ static const char *add_include_vars_lazy(request_rec *r, const char *var, const static const char *get_include_var(const char *var, include_ctx_t *ctx) { const char *val; - request_rec *r = ctx->intern->r; + request_rec *r = ctx->r; if (apr_isdigit(*var) && !var[1]) { apr_size_t idx = *var - '0'; @@ -655,22 +659,20 @@ static const char *get_include_var(const char *var, include_ctx_t *ctx) * v.s. empty strings on an empty match is deliberate. */ if (!re || !re->have_match) { - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + 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, + 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) { - /* I don't think this can happen if have_match is true. - * But let's not risk a regression by dropping this - */ + /* This particular subpattern was not used by the regex */ return NULL; } else { @@ -689,6 +691,48 @@ static const char *get_include_var(const char *var, include_ctx_t *ctx) 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') { + /* 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 * @@ -698,7 +742,7 @@ static const char *get_include_var(const char *var, include_ctx_t *ctx) 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->intern->r; + request_rec *r = ctx->r; result_item_t *result = NULL, *current = NULL; apr_size_t outlen = 0, inlen, span; char *ret = NULL, *eout = NULL; @@ -784,7 +828,7 @@ static char *ap_ssi_parse_string(include_ctx_t *ctx, const char *in, char *out, if (*++p == '{') { ep = ap_strchr_c(++p, '}'); if (!ep) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Missing '}' on " + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01331) "Missing '}' on " "variable \"%s\" in %s", p, r->filename); break; } @@ -924,7 +968,7 @@ static APR_INLINE int re_check(include_ctx_t *ctx, const char *string, compiled = ap_pregcomp(ctx->dpool, rexp, AP_REG_EXTENDED); if (!compiled) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->intern->r, "unable to " + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "unable to " "compile pattern \"%s\"", rexp); return -1; } @@ -936,7 +980,7 @@ static APR_INLINE int re_check(include_ctx_t *ctx, const char *string, 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->have_match = !ap_regexec(compiled, string, AP_MAX_REG_MATCH, re->match, 0); ap_pregfree(ctx->dpool, compiled); @@ -1030,7 +1074,7 @@ static int get_ptoken(include_ctx_t *ctx, const char **parse, token_t *token, to TYPE_TOKEN(token, TOKEN_LT); return 0; case '-': - if (**parse == 'A' && (ctx->intern->accessenable)) { + if (**parse == 'A') { TYPE_TOKEN(token, TOKEN_ACCESS); ++*parse; return 0; @@ -1123,11 +1167,10 @@ static int get_ptoken(include_ctx_t *ctx, const char **parse, token_t *token, to 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->intern->r; + request_rec *r = ctx->r; request_rec *rr = NULL; const char *error = "Invalid expression \"%s\" in file %s"; const char *parse = expr; - int was_unmatched = 0; unsigned regex = 0; *was_error = 0; @@ -1144,14 +1187,18 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error) */ CREATE_NODE(ctx, new); - was_unmatched = get_ptoken(ctx, &parse, &new->token, - (current != NULL ? ¤t->token : NULL)); - if (!parse) { - break; - } + { +#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); + DEBUG_DUMP_UNMATCHED(ctx, was_unmatched); + DEBUG_DUMP_TOKEN(ctx, &new->token); + } if (!current) { switch (new->token.type) { @@ -1335,7 +1382,7 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error) case TOKEN_AND: case TOKEN_OR: if (!current->left || !current->right) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01332) "Invalid expression \"%s\" in file %s", expr, r->filename); *was_error = 1; @@ -1400,7 +1447,7 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error) 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, + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01333) "Invalid expression \"%s\" in file %s", expr, r->filename); *was_error = 1; @@ -1435,7 +1482,7 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error) 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, + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01334) "Invalid expression \"%s\" in file %s", expr, r->filename); *was_error = 1; @@ -1483,7 +1530,7 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error) 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, + 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; @@ -1499,7 +1546,7 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error) } else { current->value = 0; - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rr->status, r, + 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); @@ -1533,6 +1580,69 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error) 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; + 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; +} /* * +-------------------------------------------------------+ @@ -1596,7 +1706,7 @@ static int find_file(request_rec *r, const char *directive, const char *tag, 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 != 0) { + 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 @@ -1626,14 +1736,14 @@ static int find_file(request_rec *r, const char *directive, const char *tag, we never attempt to "run" this sub request. */ rr = ap_sub_req_lookup_uri(tag_val, r, NULL); - if (rr->status == HTTP_OK && rr->finfo.filetype != 0) { + 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, "unable to get " + 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); @@ -1641,25 +1751,33 @@ static int find_file(request_rec *r, const char *directive, const char *tag, } } else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" " + 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; } } /* - * <!--#include virtual|file="..." [virtual|file="..."] ... --> + * <!--#include virtual|file="..." [onerror|virtual|file="..."] ... --> + * + * Output each file/virtual in turn until one of them returns an error. + * On error, ignore all further file/virtual attributes until we reach + * an onerror attribute, where we make an attempt to serve the onerror + * virtual url. If onerror fails, or no onerror is present, the default + * error string is inserted into the stream. */ static apr_status_t handle_include(include_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *bb) { request_rec *r = f->r; + char *last_error; if (!ctx->argc) { ap_log_rerror(APLOG_MARK, (ctx->flags & SSI_FLAG_PRINTING) ? APLOG_ERR : APLOG_WARNING, - 0, r, "missing argument for include element in %s", + 0, r, APLOGNO(01341) + "missing argument for include element in %s", r->filename); } @@ -1672,6 +1790,7 @@ static apr_status_t handle_include(include_ctx_t *ctx, ap_filter_t *f, return APR_SUCCESS; } + last_error = NULL; while (1) { char *tag = NULL; char *tag_val = NULL; @@ -1684,8 +1803,9 @@ static apr_status_t handle_include(include_ctx_t *ctx, ap_filter_t *f, break; } - if (strcmp(tag, "virtual") && strcmp(tag, "file")) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter " + 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; @@ -1709,8 +1829,17 @@ static apr_status_t handle_include(include_ctx_t *ctx, ap_filter_t *f, 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 { - rr = ap_sub_req_lookup_uri(parsed_string, r, f->next); + continue; } if (!error_fmt && rr->status != HTTP_OK) { @@ -1738,8 +1867,15 @@ static apr_status_t handle_include(include_ctx_t *ctx, ap_filter_t *f, if (error_fmt) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error_fmt, tag_val, - r->filename); - SSI_CREATE_ERROR_BUCKET(ctx, f, bb); + r->filename); + 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 @@ -1747,28 +1883,32 @@ static apr_status_t handle_include(include_ctx_t *ctx, ap_filter_t *f, * r->pool, so that pool must survive as long as this request. * Yes, this is a memory leak. */ - if (error_fmt) { - break; - } + } + + if (last_error) { + SSI_CREATE_ERROR_BUCKET(ctx, f, bb); } return APR_SUCCESS; } /* - * <!--#echo [encoding="..."] var="..." [encoding="..."] var="..." ... --> + * <!--#echo [decoding="..."] [encoding="..."] var="..." [decoding="..."] + * [encoding="..."] var="..." ... --> */ static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *bb) { - enum {E_NONE, E_URL, E_ENTITY} encode; + 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, "missing argument for echo element in %s", + 0, r, APLOGNO(01343) + "missing argument for echo element in %s", r->filename); } @@ -1781,8 +1921,6 @@ static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f, return APR_SUCCESS; } - encode = E_ENTITY; - while (1) { char *tag = NULL; char *tag_val = NULL; @@ -1802,17 +1940,77 @@ static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f, ctx); if (val) { - switch(encode) { - case E_NONE: - echo_text = val; - break; - case E_URL: - echo_text = ap_escape_uri(ctx->dpool, val); - break; - case E_ENTITY: - /* PR#25202: escape anything non-ascii here */ - echo_text = ap_escape_html2(ctx->dpool, val, 1); - break; + 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 (!strcasecmp(token, "none")) { + /* do nothing */ + } + else if (!strcasecmp(token, "url")) { + char *buf = apr_pstrdup(ctx->pool, echo_text); + ap_unescape_url(buf); + echo_text = buf; + } + else if (!strcasecmp(token, "urlencoded")) { + char *buf = apr_pstrdup(ctx->pool, echo_text); + ap_unescape_urlencoded(buf); + echo_text = buf; + } + else if (!strcasecmp(token, "entity")) { + char *buf = apr_pstrdup(ctx->pool, echo_text); + decodehtml(buf); + echo_text = buf; + } + else if (!strcasecmp(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 (!strcasecmp(token, "none")) { + /* do nothing */ + } + else if (!strcasecmp(token, "url")) { + echo_text = ap_escape_uri(ctx->dpool, echo_text); + } + else if (!strcasecmp(token, "urlencoded")) { + echo_text = ap_escape_urlencoded(ctx->dpool, echo_text); + } + else if (!strcasecmp(token, "entity")) { + echo_text = ap_escape_html2(ctx->dpool, echo_text, 0); + } + else if (!strcasecmp(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); @@ -1822,30 +2020,22 @@ static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f, 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")) { - if (!strcasecmp(tag_val, "none")) { - encode = E_NONE; - } - else if (!strcasecmp(tag_val, "url")) { - encode = E_URL; - } - else if (!strcasecmp(tag_val, "entity")) { - encode = E_ENTITY; - } - else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown value " - "\"%s\" to parameter \"encoding\" of tag echo in " - "%s", tag_val, r->filename); - SSI_CREATE_ERROR_BUCKET(ctx, f, bb); - break; - } + encoding = tag_val; } else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter " + 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; @@ -1869,7 +2059,8 @@ static apr_status_t handle_config(include_ctx_t *ctx, ap_filter_t *f, ap_log_rerror(APLOG_MARK, (ctx->flags & SSI_FLAG_PRINTING) ? APLOG_ERR : APLOG_WARNING, - 0, r, "missing argument for config element in %s", + 0, r, APLOGNO(01347) + "missing argument for config element in %s", r->filename); } @@ -1926,7 +2117,7 @@ static apr_status_t handle_config(include_ctx_t *ctx, ap_filter_t *f, ctx->flags &= SSI_FLAG_SIZE_ABBREV; } else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown value " + 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); @@ -1934,7 +2125,7 @@ static apr_status_t handle_config(include_ctx_t *ctx, ap_filter_t *f, } } else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter " + 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; @@ -1956,7 +2147,8 @@ static apr_status_t handle_fsize(include_ctx_t *ctx, ap_filter_t *f, ap_log_rerror(APLOG_MARK, (ctx->flags & SSI_FLAG_PRINTING) ? APLOG_ERR : APLOG_WARNING, - 0, r, "missing argument for fsize element in %s", + 0, r, APLOGNO(01350) + "missing argument for fsize element in %s", r->filename); } @@ -2043,7 +2235,8 @@ static apr_status_t handle_flastmod(include_ctx_t *ctx, ap_filter_t *f, ap_log_rerror(APLOG_MARK, (ctx->flags & SSI_FLAG_PRINTING) ? APLOG_ERR : APLOG_WARNING, - 0, r, "missing argument for flastmod element in %s", + 0, r, APLOGNO(01351) + "missing argument for flastmod element in %s", r->filename); } @@ -2104,9 +2297,10 @@ static apr_status_t handle_if(include_ctx_t *ctx, ap_filter_t *f, ap_log_rerror(APLOG_MARK, (ctx->flags & SSI_FLAG_PRINTING) ? APLOG_ERR : APLOG_WARNING, - 0, r, (ctx->argc) - ? "too many arguments for if element in %s" - : "missing expr argument for if element in %s", + 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); } @@ -2123,14 +2317,14 @@ static apr_status_t handle_if(include_ctx_t *ctx, ap_filter_t *f, 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, "unknown parameter \"%s\" " + 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, "missing expr value for if " + 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; @@ -2138,7 +2332,10 @@ static apr_status_t handle_if(include_ctx_t *ctx, ap_filter_t *f, DEBUG_PRINTF((ctx, "**** if expr=\"%s\"\n", expr)); - expr_ret = parse_expr(ctx, expr, &was_error); + 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); @@ -2173,9 +2370,10 @@ static apr_status_t handle_elif(include_ctx_t *ctx, ap_filter_t *f, if (ctx->argc != 1) { ap_log_rerror(APLOG_MARK, (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING, - 0, r, (ctx->argc) - ? "too many arguments for if element in %s" - : "missing expr argument for if element in %s", + 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); } @@ -2191,14 +2389,14 @@ static apr_status_t handle_elif(include_ctx_t *ctx, ap_filter_t *f, 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, "unknown parameter \"%s\" " + 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, "missing expr in elif " + 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; @@ -2242,7 +2440,8 @@ static apr_status_t handle_else(include_ctx_t *ctx, ap_filter_t *f, if (ctx->argc) { ap_log_rerror(APLOG_MARK, (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING, - 0, r, "else directive does not take tags in %s", + 0, r, APLOGNO(01360) + "else directive does not take tags in %s", r->filename); } @@ -2281,7 +2480,8 @@ static apr_status_t handle_endif(include_ctx_t *ctx, ap_filter_t *f, if (ctx->argc) { ap_log_rerror(APLOG_MARK, (!(ctx->if_nesting_level)) ? APLOG_ERR : APLOG_WARNING, - 0, r, "endif directive does not take tags in %s", + 0, r, APLOGNO(01361) + "endif directive does not take tags in %s", r->filename); } @@ -2308,16 +2508,19 @@ static apr_status_t handle_endif(include_ctx_t *ctx, ap_filter_t *f, 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, "missing argument for set element in %s", + 0, r, + APLOGNO(01362) "missing argument for set element in %s", r->filename); } @@ -2342,21 +2545,28 @@ static apr_status_t handle_set(include_ctx_t *ctx, ap_filter_t *f, char *tag = NULL; char *tag_val = NULL; - ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_DECODED); + 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, "variable must " + 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); @@ -2365,11 +2575,90 @@ static apr_status_t handle_set(include_ctx_t *ctx, ap_filter_t *f, 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 (!strcasecmp(token, "none")) { + /* do nothing */ + } + else if (!strcasecmp(token, "url")) { + char *buf = apr_pstrdup(ctx->pool, parsed_string); + ap_unescape_url(buf); + parsed_string = buf; + } + else if (!strcasecmp(token, "urlencoded")) { + char *buf = apr_pstrdup(ctx->pool, parsed_string); + ap_unescape_urlencoded(buf); + parsed_string = buf; + } + else if (!strcasecmp(token, "entity")) { + char *buf = apr_pstrdup(ctx->pool, parsed_string); + decodehtml(buf); + parsed_string = buf; + } + else if (!strcasecmp(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 (!strcasecmp(token, "none")) { + /* do nothing */ + } + else if (!strcasecmp(token, "url")) { + parsed_string = ap_escape_uri(ctx->dpool, parsed_string); + } + else if (!strcasecmp(token, "urlencoded")) { + parsed_string = ap_escape_urlencoded(ctx->dpool, parsed_string); + } + else if (!strcasecmp(token, "entity")) { + parsed_string = ap_escape_html2(ctx->dpool, parsed_string, 0); + } + else if (!strcasecmp(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, "Invalid tag for set " + 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; @@ -2394,7 +2683,8 @@ static apr_status_t handle_printenv(include_ctx_t *ctx, ap_filter_t *f, ap_log_rerror(APLOG_MARK, (ctx->flags & SSI_FLAG_PRINTING) ? APLOG_ERR : APLOG_WARNING, - 0, r, "printenv directive does not take tags in %s", + 0, r, + APLOGNO(01367) "printenv directive does not take tags in %s", r->filename); } @@ -2412,36 +2702,18 @@ static apr_status_t handle_printenv(include_ctx_t *ctx, ap_filter_t *f, for (i = 0; i < arr->nelts; ++i) { const char *key_text, *val_text; - char *key_val, *next; - apr_size_t k_len, v_len, kv_length; /* get key */ key_text = ap_escape_html(ctx->dpool, elts[i].key); - k_len = strlen(key_text); /* get value */ val_text = elts[i].val; - if (val_text == LAZY_VALUE) { + 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, elts[i].val); - v_len = strlen(val_text); + val_text = ap_escape_html(ctx->dpool, val_text); - /* assemble result */ - kv_length = k_len + v_len + sizeof("=\n"); - key_val = apr_palloc(ctx->pool, kv_length); - next = key_val; - - memcpy(next, key_text, k_len); - next += k_len; - *next++ = '='; - memcpy(next, val_text, v_len); - next += v_len; - *next++ = '\n'; - *next = 0; - - APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(key_val, kv_length-1, - ctx->pool, f->c->bucket_alloc)); + apr_brigade_putstrs(bb, NULL, NULL, key_text, "=", val_text, "\n", + NULL); } ctx->flush_now = 1; @@ -2784,9 +3056,9 @@ static apr_size_t find_directive(include_ctx_t *ctx, const char *data, if (!intern->directive_len) { intern->error = 1; - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, intern->r, "missing " + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(01368) "missing " "directive name in parsed document %s", - intern->r->filename); + ctx->r->filename); } else { char *sp = intern->directive; @@ -2920,11 +3192,11 @@ static apr_size_t find_argument(include_ctx_t *ctx, const char *data, intern->current_arg->name_len = 0; intern->error = 1; - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, intern->r, "missing " + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(01369) "missing " "argument name for value to tag %s in %s", - apr_pstrmemdup(intern->r->pool, intern->directive, + apr_pstrmemdup(ctx->r->pool, intern->directive, intern->directive_len), - intern->r->filename); + ctx->r->filename); return (p - data); @@ -2952,20 +3224,14 @@ static apr_size_t find_argument(include_ctx_t *ctx, const char *data, intern->current_arg->name_len); if (!intern->current_arg->name_len) { intern->error = 1; - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, intern->r, "missing " + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(01370) "missing " "argument name for value to tag %s in %s", - apr_pstrmemdup(intern->r->pool, intern->directive, + apr_pstrmemdup(ctx->r->pool, intern->directive, intern->directive_len), - intern->r->filename); + ctx->r->filename); } else { - char *sp = intern->current_arg->name; - - /* normalize the name */ - while (*sp) { - *sp = apr_tolower(*sp); - ++sp; - } + ap_str_tolower(intern->current_arg->name); } intern->state = PARSE_ARG_EQ; @@ -3126,7 +3392,6 @@ static apr_status_t send_parsed_content(ap_filter_t *f, apr_bucket_brigade *bb) /* initialization for this loop */ intern->bytes_read = 0; intern->error = 0; - intern->r = r; ctx->flush_now = 0; /* loop over the current bucket brigade */ @@ -3435,7 +3700,7 @@ static apr_status_t send_parsed_content(ap_filter_t *f, apr_bucket_brigade *bb) } } else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + 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), @@ -3472,7 +3737,7 @@ static apr_status_t send_parsed_content(ap_filter_t *f, apr_bucket_brigade *bb) } } else if (PARSE_PRE_HEAD != intern->state) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + 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) { @@ -3481,7 +3746,7 @@ static apr_status_t send_parsed_content(ap_filter_t *f, apr_bucket_brigade *bb) } if (!(ctx->flags & SSI_FLAG_PRINTING)) { - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01373) "missing closing endif directive in parsed document" " %s", r->filename); } @@ -3534,7 +3799,7 @@ static int includes_setup(ap_filter_t *f) * 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) { + if (conf->etag <= 0) { apr_table_setn(f->r->notes, "no-etag", ""); } @@ -3553,7 +3818,7 @@ static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b) &include_module); if (!(ap_allow_options(r) & OPT_INCLUDES)) { - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01374) "mod_include: Options +Includes (or IncludesNoExec) " "wasn't set, INCLUDES filter removed"); ap_remove_output_filter(f); @@ -3565,6 +3830,7 @@ static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b) /* 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); @@ -3574,23 +3840,29 @@ static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b) intern->seen_eos = 0; intern->state = PARSE_PRE_HEAD; ctx->flags = (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE); - if (ap_allow_options(r) & OPT_INCNOEXEC) { + if ((ap_allow_options(r) & OPT_INC_WITH_EXEC) == 0) { ctx->flags |= SSI_FLAG_NO_EXEC; } - intern->accessenable = conf->accessenable; + 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; - ctx->time_str = conf->default_time_fmt; + 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; - intern->undefined_echo_len = strlen(conf->undefined_echo); + 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))) { @@ -3634,7 +3906,7 @@ static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b) */ /* Must we respect the last modified header? By default, no */ - if (conf->lastmodified) { + 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. @@ -3648,7 +3920,9 @@ static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b) } /* Assure the platform supports Group protections */ - else if (((conf->xbithack == XBITHACK_FULL) + 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); @@ -3673,10 +3947,6 @@ static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b) static int include_fixup(request_rec *r) { - include_dir_config *conf; - - conf = ap_get_module_config(r->per_dir_config, &include_module); - if (r->handler && (strcmp(r->handler, "server-parsed") == 0)) { if (!r->content_type || !*r->content_type) { @@ -3692,7 +3962,13 @@ static int include_fixup(request_rec *r) } #else { - if (conf->xbithack == XBITHACK_OFF) { + 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; } @@ -3700,7 +3976,7 @@ static int include_fixup(request_rec *r) return DECLINED; } - if (!r->content_type || strcmp(r->content_type, "text/html")) { + if (!r->content_type || strncmp(r->content_type, "text/html", 9)) { return DECLINED; } } @@ -3724,19 +4000,32 @@ static int include_fixup(request_rec *r) static void *create_includes_dir_config(apr_pool_t *p, char *dummy) { - include_dir_config *result = apr_palloc(p, sizeof(include_dir_config)); + include_dir_config *result = apr_pcalloc(p, sizeof(include_dir_config)); - result->default_error_msg = DEFAULT_ERROR_MSG; - result->default_time_fmt = DEFAULT_TIME_FORMAT; - result->undefined_echo = DEFAULT_UNDEFINED_ECHO; - result->xbithack = DEFAULT_XBITHACK; - result->accessenable = DEFAULT_ACCESSENABLE; - result->lastmodified = DEFAULT_LASTMODIFIED; - result->etag = DEFAULT_ETAG; + 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; @@ -3882,14 +4171,16 @@ static const command_rec includes_cmds[] = "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("SSIAccessEnable", ap_set_flag_slot, - (void *)APR_OFFSETOF(include_dir_config, accessenable), - OR_LIMIT, "Whether testing access is enabled. Limited to 'on' or 'off'"), - AP_INIT_FLAG("SSILastModified", ap_set_flag_slot, + 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, + 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'"), @@ -3912,11 +4203,11 @@ static void register_hooks(apr_pool_t *p) AP_FTYPE_RESOURCE); } -module AP_MODULE_DECLARE_DATA include_module = +AP_DECLARE_MODULE(include) = { STANDARD20_MODULE_STUFF, create_includes_dir_config, /* dir config creater */ - NULL, /* dir merger --- default is to override */ + merge_includes_dir_config, /* dir config merger */ create_includes_server_config,/* server config */ NULL, /* merge server config */ includes_cmds, /* command apr_table_t */ diff --git a/modules/filters/mod_include.dep b/modules/filters/mod_include.dep deleted file mode 100644 index 168b2b5e..00000000 --- a/modules/filters/mod_include.dep +++ /dev/null @@ -1,36 +0,0 @@ -# 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_mmn.h"\ - "..\..\include\ap_regex.h"\ - "..\..\include\ap_release.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_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_hash.h"\ - "..\..\srclib\apr\include\apr_lib.h"\ - "..\..\srclib\apr\include\apr_mmap.h"\ - "..\..\srclib\apr\include\apr_poll.h"\ - "..\..\srclib\apr\include\apr_portable.h"\ - "..\..\srclib\apr\include\apr_strings.h"\ - ".\mod_include.h"\ - diff --git a/modules/filters/mod_include.h b/modules/filters/mod_include.h index fd5621c0..73714a29 100644 --- a/modules/filters/mod_include.h +++ b/modules/filters/mod_include.h @@ -17,7 +17,7 @@ /** * @file mod_include.h * @brief Server Side Include Filter Extension Module for Apache - * + * * @defgroup MOD_INCLUDE mod_include * @ingroup APACHE_MODS * @{ @@ -94,8 +94,12 @@ typedef struct { /* 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 *, @@ -109,7 +113,7 @@ 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, +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 deleted file mode 100644 index 3ec38789..00000000 --- a/modules/filters/mod_include.mak +++ /dev/null @@ -1,353 +0,0 @@ -# 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 00000000..a1c5fbfb --- /dev/null +++ b/modules/filters/mod_proxy_html.c @@ -0,0 +1,1266 @@ +/* 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 + +#include <ctype.h> + +/* libxml2 */ +#include <libxml/HTMLparser.h> + +#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_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; + size_t 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; +} saxctxt; + + +#define NORM_LC 0x1 +#define NORM_MSSLASH 0x2 +#define NORM_RESET 0x4 +static htmlSAXHandler sax; + +typedef enum { ATTR_IGNORE, ATTR_URI, ATTR_EVENT } rewrite_t; + +static const char* const fpi_html = + "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n"; +static const char* const fpi_html_legacy = + "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"; +static const char* const fpi_xhtml = + "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"; +static const char* const fpi_xhtml_legacy = + "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; +static const char* const html_etag = ">"; +static const char* const xhtml_etag = " />"; +/*#define DEFAULT_DOCTYPE fpi_html */ +static const char* const DEFAULT_DOCTYPE = ""; +#define DEFAULT_ETAG html_etag + +static void normalise(unsigned int flags, char* str) +{ + char* p; + if (flags & NORM_LC) + for (p = str; *p; ++p) + if (isupper(*p)) + *p = tolower(*p); + + if (flags & NORM_MSSLASH) + for (p = ap_strchr(str, '\\'); p; p = ap_strchr(p+1, '\\')) + *p = '/'; + +} +#define consume_buffer(ctx,inbuf,bytes,flag) \ + htmlParseChunk(ctx->parser, inbuf, bytes, flag) + +#define AP_fwrite(ctx,inbuf,bytes,flush) \ + ap_fwrite(ctx->f->next, ctx->bb, inbuf, bytes); + +/* This is always utf-8 on entry. We can convert charset within FLUSH */ +#define FLUSH AP_fwrite(ctx, (chars+begin), (i-begin), 0); begin = i+1 +static void pcharacters(void* ctxt, const xmlChar *uchars, int length) +{ + const char* chars = (const char*) uchars; + saxctxt* ctx = (saxctxt*) ctxt; + int i; + int begin; + for (begin=i=0; i<length; i++) { + switch (chars[i]) { + case '&' : FLUSH; ap_fputs(ctx->f->next, ctx->bb, "&"); 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, "<!--", 4); + pappend(ctx, chars, strlen(chars)); + pappend(ctx, "-->", 3); + } + else { + ap_fputs(ctx->f->next, ctx->bb, "<!--"); + AP_fwrite(ctx, chars, strlen(chars), 1); + ap_fputs(ctx->f->next, ctx->bb, "-->"); + } +} +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) + || (ctx->cfg->doctype == fpi_xhtml)) { + /* enforce html legacy */ + if (!desc) + return; + } + /* TODO - implement HTML "allowed here" using the stack */ + /* nah. Keeping the stack is too much overhead */ + + if (ctx->offset > 0) { + dump_content(ctx); + ctx->offset = 0; /* having dumped it, we can re-use the memory */ + } + if (!desc || !desc->empty) { + ap_fprintf(ctx->f->next, ctx->bb, "</%s>", name); + } +} +static void pstartElement(void* ctxt, const xmlChar* uname, + const xmlChar** uattrs) +{ + int required_attrs; + int num_match; + size_t offs, len; + char* subs; + rewrite_t is_uri; + const char** a; + urlmap* m; + size_t s_to, s_from, match; + char* found; + saxctxt* ctx = (saxctxt*) ctxt; + size_t nmatch; + ap_regmatch_t pmatch[10]; +#ifndef GO_FASTER + int verbose = APLOGrtrace1(ctx->f->r); +#endif + apr_array_header_t *linkattrs; + int i; + const char* name = (const char*) uname; + const char** attrs = (const char**) uattrs; + const htmlElemDesc* desc = htmlTagLookup(uname); + urlmap* themap = ctx->map; +#ifdef HAVE_STACK + const void** descp; +#endif + int enforce = 0; + if ((ctx->cfg->doctype == fpi_html) || (ctx->cfg->doctype == fpi_xhtml)) { + /* enforce html */ + enforce = 2; + if (!desc || desc->depr) + return; + + } + else if ((ctx->cfg->doctype == fpi_html) + || (ctx->cfg->doctype == fpi_xhtml)) { + enforce = 1; + /* enforce html legacy */ + if (!desc) { + return; + } + } + if (!desc && enforce) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->f->r, APLOGNO(01416) + "Bogus HTML element %s dropped", name); + return; + } + if (desc && desc->depr && (enforce == 2)) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->f->r, APLOGNO(01417) + "Deprecated HTML element %s dropped", name); + return; + } +#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) +{ + 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 (!ap_regexec(seek_meta, buf+offs, 2, pmatch, 0)) { + header = NULL; + content = NULL; + p = buf+offs+pmatch[1].rm_eo; + while (!isalpha(*++p)); + for (q = p; isalnum(*q) || (*q == '-'); ++q); + header = apr_pstrndup(r->pool, p, q-p); + if (strncasecmp(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 (*p && isspace(*p)) + ++p; + if (*p != '=') + continue; + while (*p && isspace(*++p)); + if ((*p == '\'') || (*p == '"')) { + delim = *p++; + for (q = p; *q != delim; ++q); + } else { + for (q = p; *q && !isspace(*q) && (*q != '>'); ++q); + } + content = apr_pstrndup(r->pool, p, q-p); + break; + } + } + } + else if (!strncasecmp(header, "Content-Type", 12)) { + ret = apr_palloc(r->pool, sizeof(meta)); + ret->start = pmatch[0].rm_so; + ret->end = 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 (;;) { + start = str; + if (start = ap_strstr_c(start, "${"), start == NULL) + break; + + if (end = ap_strchr_c(start+2, '}'), end == NULL) + break; + + delim = ap_strchr_c(start, '|'); + before = apr_pstrndup(r->pool, str, start-str); + after = end+1; + if (delim) { + var = apr_pstrndup(r->pool, start+2, delim-start-2); + } + else { + var = apr_pstrndup(r->pool, start+2, end-start-2); + } + replacement = apr_table_get(r->subprocess_env, var); + if (!replacement) { + if (delim) + replacement = apr_pstrndup(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 (strncasecmp(f->r->content_type, "text/html", 9) && + strncasecmp(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 int 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, buf, 0, 1); + } + APR_BRIGADE_INSERT_TAIL(ctxt->bb, + apr_bucket_eos_create(ctxt->bb->bucket_alloc)); + ap_pass_brigade(ctxt->f->next, 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; + 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); + ctxt->parser = htmlCreatePushParserCtxt(&sax, ctxt, buf, + 4, 0, enc); + buf += 4; + bytes -= 4; + if (ctxt->parser == NULL) { + apr_status_t rv = ap_pass_brigade(f->next, bb); + ap_remove_output_filter(f); + return rv; + } + 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); + 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 { + cfg->doctype = apr_pstrdup(cmd->pool, 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 (!strcmp(arg, "lowercase")) + cfg->flags |= NORM_LC; + else if (!strcmp(arg, "dospath")) + cfg->flags |= NORM_MSSLASH; + else if (!strcmp(arg, "reset")) + cfg->flags |= NORM_RESET; + } + return NULL; +} +static const char* set_events(cmd_parms* cmd, void* CFG, const char* arg) +{ + tattr* attr; + proxy_html_conf* cfg = CFG; + if (cfg->events == NULL) + cfg->events = apr_array_make(cmd->pool, 20, sizeof(tattr)); + attr = apr_array_push(cfg->events); + attr->val = arg; + return NULL; +} +static const char* set_links(cmd_parms* cmd, void* CFG, + const char* elt, const char* att) +{ + apr_array_header_t* attrs; + tattr* attr; + proxy_html_conf* cfg = CFG; + + if (cfg->links == NULL) + cfg->links = apr_hash_make(cmd->pool); + + attrs = apr_hash_get(cfg->links, elt, APR_HASH_KEY_STRING); + if (!attrs) { + attrs = apr_array_make(cmd->pool, 2, sizeof(tattr*)); + apr_hash_set(cfg->links, elt, APR_HASH_KEY_STRING, attrs); + } + attr = apr_array_push(attrs); + attr->val = att; + return NULL; +} +static const command_rec proxy_html_cmds[] = { + AP_INIT_ITERATE("ProxyHTMLEvents", set_events, NULL, + RSRC_CONF|ACCESS_CONF, + "Strings to be treated as scripting events"), + AP_INIT_ITERATE2("ProxyHTMLLinks", set_links, NULL, + RSRC_CONF|ACCESS_CONF, "Declare HTML Attributes"), + AP_INIT_RAW_ARGS("ProxyHTMLURLMap", set_urlmap, NULL, + RSRC_CONF|ACCESS_CONF, "Map URL From To"), + AP_INIT_TAKE12("ProxyHTMLDoctype", set_doctype, NULL, + RSRC_CONF|ACCESS_CONF, "(HTML|XHTML) [Legacy]"), + AP_INIT_ITERATE("ProxyHTMLFixups", set_flags, NULL, + RSRC_CONF|ACCESS_CONF, "Options are lowercase, dospath"), + AP_INIT_FLAG("ProxyHTMLMeta", ap_set_flag_slot, + (void*)APR_OFFSETOF(proxy_html_conf, metafix), + RSRC_CONF|ACCESS_CONF, "Fix META http-equiv elements"), + AP_INIT_FLAG("ProxyHTMLInterp", ap_set_flag_slot, + (void*)APR_OFFSETOF(proxy_html_conf, interp), + RSRC_CONF|ACCESS_CONF, + "Support interpolation and conditions in URLMaps"), + AP_INIT_FLAG("ProxyHTMLExtended", ap_set_flag_slot, + (void*)APR_OFFSETOF(proxy_html_conf, extfix), + RSRC_CONF|ACCESS_CONF, "Map URLs in Javascript and CSS"), + AP_INIT_FLAG("ProxyHTMLStripComments", ap_set_flag_slot, + (void*)APR_OFFSETOF(proxy_html_conf, strip_comments), + RSRC_CONF|ACCESS_CONF, "Strip out comments"), + AP_INIT_TAKE1("ProxyHTMLBufSize", ap_set_int_slot, + (void*)APR_OFFSETOF(proxy_html_conf, bufsz), + RSRC_CONF|ACCESS_CONF, "Buffer size"), + AP_INIT_TAKE1("ProxyHTMLCharsetOut", ap_set_string_slot, + (void*)APR_OFFSETOF(proxy_html_conf, charset_out), + RSRC_CONF|ACCESS_CONF, "Usage: ProxyHTMLCharsetOut charset"), + AP_INIT_FLAG("ProxyHTMLEnable", ap_set_flag_slot, + (void*)APR_OFFSETOF(proxy_html_conf, enabled), + RSRC_CONF|ACCESS_CONF, + "Enable proxy-html and xml2enc filters"), + { NULL } +}; +static int mod_proxy_html(apr_pool_t* p, apr_pool_t* p1, apr_pool_t* p2) +{ + seek_meta = ap_pregcomp(p, "<meta[^>]*(http-equiv)[^>]*>", + AP_REG_EXTENDED|AP_REG_ICASE); + seek_content = apr_strmatch_precompile(p, "content", 0); + memset(&sax, 0, sizeof(htmlSAXHandler)); + sax.startElement = pstartElement; + sax.endElement = pendElement; + sax.characters = pcharacters; + sax.comment = pcomment; + sax.cdataBlock = pcdata; + xml2enc_charset = APR_RETRIEVE_OPTIONAL_FN(xml2enc_charset); + xml2enc_filter = APR_RETRIEVE_OPTIONAL_FN(xml2enc_filter); + if (!xml2enc_charset) { + ap_log_perror(APLOG_MARK, APLOG_NOTICE, 0, p2, APLOGNO(01425) + "I18n support in mod_proxy_html requires mod_xml2enc. " + "Without it, non-ASCII characters in proxied pages are " + "likely to display incorrectly."); + } + + /* old_expr only needs to last the life of the config phase */ + old_expr = ap_rxplus_compile(p1, "s/^(!)?(\\w+)((=)(.+))?$/reqenv('$2')$1$4'$5'/"); + return OK; +} +static void proxy_html_insert(request_rec* r) +{ + proxy_html_conf* cfg; + cfg = ap_get_module_config(r->per_dir_config, &proxy_html_module); + if (cfg->enabled) { + if (xml2enc_filter) + xml2enc_filter(r, NULL, ENCIO_INPUT_CHECKS); + ap_add_output_filter("proxy-html", NULL, r, r->connection); + } +} +static void proxy_html_hooks(apr_pool_t* p) +{ + static const char* aszSucc[] = { "mod_filter.c", NULL }; + ap_register_output_filter_protocol("proxy-html", proxy_html_filter, + NULL, AP_FTYPE_RESOURCE, + AP_FILTER_PROTO_CHANGE|AP_FILTER_PROTO_CHANGE_LENGTH); + /* move this to pre_config so old_expr is available to interpret + * old-style conditions on URL maps. + */ + ap_hook_pre_config(mod_proxy_html, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_insert_filter(proxy_html_insert, NULL, aszSucc, APR_HOOK_MIDDLE); +} +module AP_MODULE_DECLARE_DATA proxy_html_module = { + 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.dsp b/modules/filters/mod_proxy_html.dsp new file mode 100644 index 00000000..149166cb --- /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" +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 /i /d "_DEBUG" "../../include" /i "../../srclib/apr/include" /I "../../srclib/apr-util/include" /i "../../srclib/libxml2/include" +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_ratelimit.c b/modules/filters/mod_ratelimit.c new file mode 100644 index 00000000..028de361 --- /dev/null +++ b/modules/filters/mod_ratelimit.c @@ -0,0 +1,313 @@ +/* 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_ERROR, + RATE_LIMIT, + RATE_FULLSPEED +} rl_state_e; + +typedef struct rl_ctx_t +{ + int speed; + int chunk_size; + rl_state_e state; + apr_bucket_brigade *tmpbb; + apr_bucket_brigade *holdingbb; +} rl_ctx_t; + +#if 0 +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, + "brigade: [%d] %s", i, e->type->name); + + } +} +#endif + +static apr_status_t +rate_limit_filter(ap_filter_t *f, apr_bucket_brigade *input_bb) +{ + apr_status_t rv = APR_SUCCESS; + rl_ctx_t *ctx = f->ctx; + apr_bucket *fb; + int do_sleep = 0; + apr_bucket_alloc_t *ba = f->r->connection->bucket_alloc; + apr_bucket_brigade *bb = input_bb; + + if (f->c->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01454) "rl: conn aborted"); + apr_brigade_cleanup(bb); + return APR_ECONNABORTED; + } + + if (ctx == NULL) { + + const char *rl = NULL; + + /* no subrequests. */ + if (f->r->main != NULL) { + ap_remove_output_filter(f); + return ap_pass_brigade(f->next, bb); + } + + 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); + } + + /* first run, init stuff */ + ctx = apr_palloc(f->r->pool, sizeof(rl_ctx_t)); + f->ctx = ctx; + ctx->speed = 0; + ctx->state = RATE_LIMIT; + + /* rl is in kilo bytes / second */ + ctx->speed = atoi(rl) * 1024; + + if (ctx->speed == 0) { + /* remove ourselves */ + ap_remove_output_filter(f); + return ap_pass_brigade(f->next, bb); + } + + /* 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); + } + + while (ctx->state != RATE_ERROR && + (!APR_BRIGADE_EMPTY(bb) || !APR_BRIGADE_EMPTY(ctx->holdingbb))) { + apr_bucket *e; + + if (!APR_BRIGADE_EMPTY(ctx->holdingbb)) { + APR_BRIGADE_CONCAT(bb, ctx->holdingbb); + apr_brigade_cleanup(ctx->holdingbb); + } + + while (ctx->state == RATE_FULLSPEED && !APR_BRIGADE_EMPTY(bb)) { + /* 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_bucket *f; + f = APR_RING_LAST(&bb->list); + APR_RING_UNSPLICE(e, f, link); + APR_RING_SPLICE_TAIL(&ctx->holdingbb->list, e, f, + apr_bucket, link); + ctx->state = RATE_LIMIT; + break; + } + } + + if (f->c->aborted) { + apr_brigade_cleanup(bb); + ctx->state = RATE_ERROR; + break; + } + + fb = apr_bucket_flush_create(ba); + APR_BRIGADE_INSERT_TAIL(bb, fb); + rv = ap_pass_brigade(f->next, bb); + + if (rv != APR_SUCCESS) { + ctx->state = RATE_ERROR; + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, APLOGNO(01455) + "rl: full speed brigade pass failed."); + } + } + + while (ctx->state == RATE_LIMIT && !APR_BRIGADE_EMPTY(bb)) { + for (e = APR_BRIGADE_FIRST(bb); + e != APR_BRIGADE_SENTINEL(bb); e = APR_BUCKET_NEXT(e)) { + if (AP_RL_BUCKET_IS_START(e)) { + apr_bucket *f; + f = APR_RING_LAST(&bb->list); + APR_RING_UNSPLICE(e, f, link); + APR_RING_SPLICE_TAIL(&ctx->holdingbb->list, e, f, + apr_bucket, link); + ctx->state = RATE_FULLSPEED; + break; + } + } + + while (!APR_BRIGADE_EMPTY(bb)) { + apr_bucket *stop_point; + apr_off_t len = 0; + + if (f->c->aborted) { + apr_brigade_cleanup(bb); + ctx->state = RATE_ERROR; + break; + } + + if (do_sleep) { + apr_sleep(RATE_INTERVAL_MS * 1000); + } + else { + do_sleep = 1; + } + + apr_brigade_length(bb, 1, &len); + + rv = apr_brigade_partition(bb, ctx->chunk_size, &stop_point); + if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) { + ctx->state = RATE_ERROR; + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01456) + "rl: partition failed."); + break; + } + + if (stop_point != APR_BRIGADE_SENTINEL(bb)) { + apr_bucket *f; + apr_bucket *e = APR_BUCKET_PREV(stop_point); + f = APR_RING_FIRST(&bb->list); + APR_RING_UNSPLICE(f, e, link); + APR_RING_SPLICE_HEAD(&ctx->tmpbb->list, f, e, apr_bucket, + link); + } + else { + APR_BRIGADE_CONCAT(ctx->tmpbb, bb); + } + + fb = apr_bucket_flush_create(ba); + + APR_BRIGADE_INSERT_TAIL(ctx->tmpbb, fb); + +#if 0 + brigade_dump(f->r, ctx->tmpbb); + brigade_dump(f->r, bb); +#endif + + rv = ap_pass_brigade(f->next, ctx->tmpbb); + apr_brigade_cleanup(ctx->tmpbb); + + if (rv != APR_SUCCESS) { + ctx->state = RATE_ERROR; + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01457) + "rl: brigade pass failed."); + break; + } + } + } + } + + return rv; +} + + +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_PROTOCOL + 3); +} + +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.dsp b/modules/filters/mod_ratelimit.dsp new file mode 100644 index 00000000..99bb5950 --- /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 00000000..6c69bb1a --- /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_reflector.c b/modules/filters/mod_reflector.c new file mode 100644 index 00000000..469df8e8 --- /dev/null +++ b/modules/filters/mod_reflector.c @@ -0,0 +1,232 @@ +/* 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 offset; + + apr_strtoff(&offset, content_length, NULL, 10); + ap_set_content_length(r, offset); + + } + + /* 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) { + if (status == AP_FILTER_ERROR) { + apr_brigade_destroy(bbin); + return status; + } + else { + apr_brigade_destroy(bbin); + return 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 HTTP_INTERNAL_SERVER_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.dsp b/modules/filters/mod_reflector.dsp new file mode 100644 index 00000000..789d07f4 --- /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="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_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="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_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_reqtimeout.c b/modules/filters/mod_reqtimeout.c index bc04e684..e71a3251 100644 --- a/modules/filters/mod_reqtimeout.c +++ b/modules/filters/mod_reqtimeout.c @@ -14,7 +14,6 @@ * limitations under the License. */ -#define CORE_PRIVATE #include "httpd.h" #include "http_config.h" #include "http_request.h" @@ -29,6 +28,14 @@ module AP_MODULE_DECLARE_DATA reqtimeout_module; +#define UNSET -1 +#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 header_timeout; /* timeout for reading the req hdrs in secs */ @@ -57,6 +64,8 @@ typedef struct } reqtimeout_con_cfg; static const char *const reqtimeout_filter_name = "reqtimeout"; +static int default_header_rate_factor; +static int default_body_rate_factor; static void extend_timeout(reqtimeout_con_cfg *ccfg, apr_bucket_brigade *bb) { @@ -76,12 +85,15 @@ static void extend_timeout(reqtimeout_con_cfg *ccfg, apr_bucket_brigade *bb) } static apr_status_t check_time_left(reqtimeout_con_cfg *ccfg, - apr_time_t *time_left_p) + apr_time_t *time_left_p, + apr_time_t now) { - *time_left_p = ccfg->timeout_at - apr_time_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); } @@ -93,9 +105,9 @@ 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; + const char *str; + apr_size_t len; + apr_status_t rv; if (APR_BUCKET_IS_EOS(b)) return APR_SUCCESS; @@ -160,9 +172,9 @@ static apr_status_t reqtimeout_filter(ap_filter_t *f, apr_off_t readbytes) { apr_time_t time_left; - apr_time_t now; + apr_time_t now = 0; apr_status_t rv; - apr_interval_time_t saved_sock_timeout = -1; + apr_interval_time_t saved_sock_timeout = UNSET; reqtimeout_con_cfg *ccfg = f->ctx; if (ccfg->in_keep_alive) { @@ -171,9 +183,9 @@ static apr_status_t reqtimeout_filter(ap_filter_t *f, return ap_get_brigade(f->next, bb, mode, block, readbytes); } - now = apr_time_now(); if (ccfg->new_timeout > 0) { /* set new timeout */ + now = apr_time_now(); ccfg->timeout_at = now + apr_time_from_sec(ccfg->new_timeout); ccfg->new_timeout = 0; if (ccfg->new_max_timeout > 0) { @@ -187,10 +199,10 @@ static apr_status_t reqtimeout_filter(ap_filter_t *f, } if (!ccfg->socket) { - ccfg->socket = ap_get_module_config(f->c->conn_config, &core_module); + ccfg->socket = ap_get_conn_socket(f->c); } - rv = check_time_left(ccfg, &time_left); + rv = check_time_left(ccfg, &time_left, now); if (rv != APR_SUCCESS) goto out; @@ -272,7 +284,7 @@ static apr_status_t reqtimeout_filter(ap_filter_t *f, if (rv != APR_SUCCESS) break; - rv = check_time_left(ccfg, &time_left); + rv = check_time_left(ccfg, &time_left, 0); if (rv != APR_SUCCESS) break; @@ -298,7 +310,7 @@ static apr_status_t reqtimeout_filter(ap_filter_t *f, out: if (APR_STATUS_IS_TIMEUP(rv)) { - ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c, + 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 @@ -326,17 +338,25 @@ static int reqtimeout_init(conn_rec *c) cfg = ap_get_module_config(c->base_server->module_config, &reqtimeout_module); AP_DEBUG_ASSERT(cfg != NULL); - if (cfg->header_timeout <= 0 && cfg->body_timeout <= 0) { - /* not configured for this vhost */ + if (cfg->header_timeout == 0 && cfg->body_timeout == 0) { + /* disabled for this vhost */ return DECLINED; } ccfg = apr_pcalloc(c->pool, sizeof(reqtimeout_con_cfg)); - ccfg->new_timeout = cfg->header_timeout; - ccfg->new_max_timeout = cfg->header_max_timeout; ccfg->type = "header"; - ccfg->min_rate = cfg->header_min_rate; - ccfg->rate_factor = cfg->header_rate_factor; + if (cfg->header_timeout != UNSET) { + ccfg->new_timeout = cfg->header_timeout; + ccfg->new_max_timeout = cfg->header_max_timeout; + ccfg->min_rate = cfg->header_min_rate; + ccfg->rate_factor = cfg->header_rate_factor; + } + else { + ccfg->new_timeout = MRT_DEFAULT_HEADER_TIMEOUT; + ccfg->new_max_timeout = MRT_DEFAULT_HEADER_MAX_TIMEOUT; + ccfg->min_rate = MRT_DEFAULT_HEADER_MIN_RATE; + ccfg->rate_factor = default_header_rate_factor; + } ap_set_module_config(c->conn_config, &reqtimeout_module, ccfg); ap_add_input_filter("reqtimeout", ccfg, NULL, c); @@ -350,25 +370,29 @@ static int reqtimeout_after_headers(request_rec *r) reqtimeout_con_cfg *ccfg = ap_get_module_config(r->connection->conn_config, &reqtimeout_module); - if (ccfg == NULL) { - /* not configured for this connection */ + if (ccfg == NULL || r->method_number == M_CONNECT) { + /* either disabled for this connection or a CONNECT request */ return OK; } - cfg = ap_get_module_config(r->connection->base_server->module_config, &reqtimeout_module); AP_DEBUG_ASSERT(cfg != NULL); ccfg->timeout_at = 0; ccfg->max_timeout_at = 0; - if (r->method_number != M_CONNECT) { - ccfg->new_timeout = cfg->body_timeout; + ccfg->type = "body"; + if (cfg->body_timeout != UNSET) { + ccfg->new_timeout = cfg->body_timeout; ccfg->new_max_timeout = cfg->body_max_timeout; - ccfg->min_rate = cfg->body_min_rate; - ccfg->rate_factor = cfg->body_rate_factor; - ccfg->type = "body"; + ccfg->min_rate = cfg->body_min_rate; + ccfg->rate_factor = cfg->body_rate_factor; + } + else { + ccfg->new_timeout = MRT_DEFAULT_BODY_TIMEOUT; + ccfg->new_max_timeout = MRT_DEFAULT_BODY_MAX_TIMEOUT; + ccfg->min_rate = MRT_DEFAULT_BODY_MIN_RATE; + ccfg->rate_factor = default_body_rate_factor; } - return OK; } @@ -390,12 +414,19 @@ static int reqtimeout_after_body(request_rec *r) ccfg->timeout_at = 0; ccfg->max_timeout_at = 0; ccfg->in_keep_alive = 1; - ccfg->new_timeout = cfg->header_timeout; - ccfg->new_max_timeout = cfg->header_max_timeout; - ccfg->min_rate = cfg->header_min_rate; - ccfg->rate_factor = cfg->header_rate_factor; - ccfg->type = "header"; + if (ccfg->new_timeout != UNSET) { + ccfg->new_timeout = cfg->header_timeout; + ccfg->new_max_timeout = cfg->header_max_timeout; + ccfg->min_rate = cfg->header_min_rate; + ccfg->rate_factor = cfg->header_rate_factor; + } + else { + ccfg->new_timeout = MRT_DEFAULT_HEADER_TIMEOUT; + ccfg->new_max_timeout = MRT_DEFAULT_HEADER_MAX_TIMEOUT; + ccfg->min_rate = MRT_DEFAULT_HEADER_MIN_RATE; + ccfg->rate_factor = default_header_rate_factor; + } return OK; } @@ -404,17 +435,17 @@ static void *reqtimeout_create_srv_config(apr_pool_t *p, server_rec *s) { reqtimeout_srv_cfg *cfg = apr_pcalloc(p, sizeof(reqtimeout_srv_cfg)); - cfg->header_timeout = -1; - cfg->header_max_timeout = -1; - cfg->header_min_rate = -1; - cfg->body_timeout = -1; - cfg->body_max_timeout = -1; - cfg->body_min_rate = -1; + cfg->header_timeout = UNSET; + cfg->header_max_timeout = UNSET; + cfg->header_min_rate = UNSET; + cfg->body_timeout = UNSET; + cfg->body_max_timeout = UNSET; + cfg->body_min_rate = UNSET; return cfg; } -#define MERGE_INT(cfg, b, a, val) cfg->val = (a->val == -1) ? b->val : a->val; +#define MERGE_INT(cfg, b, a, val) cfg->val = (a->val == UNSET) ? b->val : a->val; static void *reqtimeout_merge_srv_config(apr_pool_t *p, void *base_, void *add_) { reqtimeout_srv_cfg *base = base_; @@ -428,11 +459,10 @@ static void *reqtimeout_merge_srv_config(apr_pool_t *p, void *base_, void *add_) MERGE_INT(cfg, base, add, body_max_timeout); MERGE_INT(cfg, base, add, body_min_rate); - cfg->header_rate_factor = (cfg->header_min_rate == -1) ? base->header_rate_factor : - add->header_rate_factor; - cfg->body_rate_factor = (cfg->body_min_rate == -1) ? base->body_rate_factor : - add->body_rate_factor; - + cfg->header_rate_factor = (cfg->header_min_rate == UNSET) ? + base->header_rate_factor : add->header_rate_factor; + cfg->body_rate_factor = (cfg->body_min_rate == UNSET) ? + base->body_rate_factor : add->body_rate_factor; return cfg; } @@ -471,7 +501,7 @@ static const char *set_reqtimeout_param(reqtimeout_srv_cfg *conf, else { return "Unknown RequestReadTimeout parameter"; } - + if ((rate_str = ap_strcasestr(val, ",minrate="))) { initial_str = apr_pstrndup(p, val, rate_str - val); rate_str += strlen(",minrate="); @@ -488,7 +518,7 @@ static const char *set_reqtimeout_param(reqtimeout_srv_cfg *conf, if (ret) return ret; } - + ret = parse_int(p, initial_str, &initial); } else { @@ -496,7 +526,7 @@ static const char *set_reqtimeout_param(reqtimeout_srv_cfg *conf, return "Must set MinRate option if using timeout range"; ret = parse_int(p, val, &initial); } - + if (ret) return ret; @@ -527,12 +557,12 @@ static const char *set_reqtimeouts(cmd_parms *cmd, void *mconfig, 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->pool, &arg); + + word = ap_getword_conf(cmd->temp_pool, &arg); val = strchr(word, '='); if (!val) { return "Invalid RequestReadTimeout parameter. Parameter must be " @@ -542,14 +572,14 @@ static const char *set_reqtimeouts(cmd_parms *cmd, void *mconfig, *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) @@ -575,6 +605,13 @@ static void reqtimeout_hooks(apr_pool_t *pool) APR_HOOK_MIDDLE); ap_hook_log_transaction(reqtimeout_after_body, NULL, NULL, APR_HOOK_MIDDLE); + +#if MRT_DEFAULT_HEADER_MIN_RATE > 0 + default_header_rate_factor = apr_time_from_sec(1) / MRT_DEFAULT_HEADER_MIN_RATE; +#endif +#if MRT_DEFAULT_BODY_MIN_RATE > 0 + default_body_rate_factor = apr_time_from_sec(1) / MRT_DEFAULT_BODY_MIN_RATE; +#endif } static const command_rec reqtimeout_cmds[] = { @@ -584,7 +621,7 @@ static const command_rec reqtimeout_cmds[] = { {NULL} }; -module AP_MODULE_DECLARE_DATA reqtimeout_module = { +AP_DECLARE_MODULE(reqtimeout) = { STANDARD20_MODULE_STUFF, NULL, /* create per-dir config structures */ NULL, /* merge per-dir config structures */ diff --git a/modules/filters/mod_reqtimeout.dep b/modules/filters/mod_reqtimeout.dep deleted file mode 100644 index 7a96e657..00000000 --- a/modules/filters/mod_reqtimeout.dep +++ /dev/null @@ -1,32 +0,0 @@ -# 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_mmn.h"\ - "..\..\include\ap_regex.h"\ - "..\..\include\ap_release.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_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_hash.h"\ - "..\..\srclib\apr\include\apr_mmap.h"\ - "..\..\srclib\apr\include\apr_poll.h"\ - "..\..\srclib\apr\include\apr_portable.h"\ - "..\..\srclib\apr\include\apr_strings.h"\ - "..\..\srclib\apr\include\apr_version.h"\ - diff --git a/modules/filters/mod_reqtimeout.mak b/modules/filters/mod_reqtimeout.mak deleted file mode 100644 index 3839f834..00000000 --- a/modules/filters/mod_reqtimeout.mak +++ /dev/null @@ -1,353 +0,0 @@ -# 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 00000000..ae59ab63 --- /dev/null +++ b/modules/filters/mod_request.c @@ -0,0 +1,396 @@ +/* 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; + char *endstr = NULL; + 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 (excluding leading space) (the endstr checks) + * and a negative number. */ + if (apr_strtoff(&ctx->remaining, lenp, &endstr, 10) + || endstr == lenp || *endstr || ctx->remaining < 0) { + + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01411) + "Invalid Content-Length"); + + 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.dsp b/modules/filters/mod_request.dsp new file mode 100644 index 00000000..4032e008 --- /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_sed.c b/modules/filters/mod_sed.c new file mode 100644 index 00000000..dd776c48 --- /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; + int 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 + * happend 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, "%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, int 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) +{ + int 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, int sz) +{ + /* dummy is basically filter context. Context is passed during invocation + * of sed_eval_buffer + */ + int 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); + /* old buffer is now used so allocate new buffer */ + alloc_outbuf(ctx); + /* if size is bigger than the allocated buffer directly add to output + * brigade */ + if ((status == APR_SUCCESS) && (sz >= ctx->bufsize)) { + char* newbuf = apr_pmemdup(ctx->tpool, buf, sz); + status = append_bucket(ctx, newbuf, sz); + /* pool might get clear after append_bucket */ + if (ctx->outbuf == NULL) { + alloc_outbuf(ctx); + } + } + else { + memcpy(ctx->curoutbuf, buf, sz); + ctx->curoutbuf += sz; + } + } + 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); + } + 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; + 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 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. + */ + for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb);) { + const char *buf = NULL; + apr_size_t bytes = 0; + if (APR_BUCKET_IS_EOS(b)) { + apr_bucket *b1 = APR_BUCKET_NEXT(b); + /* Now clean up the internal sed buffer */ + sed_finalize_eval(&ctx->eval, ctx); + status = flush_output_buffer(ctx); + if (status != APR_SUCCESS) { + clear_ctxpool(ctx); + return status; + } + APR_BUCKET_REMOVE(b); + /* Insert the eos bucket to ctx->bb brigade */ + APR_BRIGADE_INSERT_TAIL(ctx->bb, b); + b = b1; + } + else if (APR_BUCKET_IS_FLUSH(b)) { + apr_bucket *b1 = APR_BUCKET_NEXT(b); + APR_BUCKET_REMOVE(b); + status = flush_output_buffer(ctx); + if (status != APR_SUCCESS) { + clear_ctxpool(ctx); + return status; + } + APR_BRIGADE_INSERT_TAIL(ctx->bb, b); + b = b1; + } + else if (APR_BUCKET_IS_METADATA(b)) { + b = APR_BUCKET_NEXT(b); + } + else if (apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ) + == APR_SUCCESS) { + apr_bucket *b1 = APR_BUCKET_NEXT(b); + status = sed_eval_buffer(&ctx->eval, buf, bytes, ctx); + if (status != APR_SUCCESS) { + clear_ctxpool(ctx); + return status; + } + APR_BUCKET_REMOVE(b); + apr_bucket_delete(b); + b = b1; + } + else { + apr_bucket *b1 = APR_BUCKET_NEXT(b); + APR_BUCKET_REMOVE(b); + b = b1; + } + } + apr_brigade_cleanup(bb); + status = flush_output_buffer(ctx); + if (status != APR_SUCCESS) { + clear_ctxpool(ctx); + return status; + } + if (!APR_BRIGADE_EMPTY(ctx->bb)) { + 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 + * atleast one bucket 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) + 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.dsp b/modules/filters/mod_sed.dsp new file mode 100644 index 00000000..800dfa10 --- /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 substitute: +!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="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_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="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_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_substitute.c b/modules/filters/mod_substitute.c index 07030403..15cd8ee4 100644 --- a/modules/filters/mod_substitute.c +++ b/modules/filters/mod_substitute.c @@ -21,11 +21,13 @@ #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 @@ -78,17 +80,11 @@ static void *merge_substitute_dcfg(apr_pool_t *p, void *basev, void *overv) } #define AP_MAX_BUCKETS 1000 - -#define SEDSCAT(s1, s2, pool, buff, blen, repl) do { \ - if (!s1) { \ - s1 = apr_pstrmemdup(pool, buff, blen); \ - } \ - else { \ - s2 = apr_pstrmemdup(pool, buff, blen); \ - s1 = apr_pstrcat(pool, s1, s2, NULL); \ - } \ - s1 = apr_pstrcat(pool, s1, repl, NULL); \ -} while (0) +/* + * We want to limit the memory usage in a way that is predictable. Therefore + * we limit the resulting length of the line to this value. + */ +#define AP_SUBST_MAX_LINE_LENGTH (128*MAX_STRING_LEN) #define SEDRMPATBCKT(b, offset, tmp_b, patlen) do { \ apr_bucket_split(b, offset); \ @@ -98,25 +94,19 @@ static void *merge_substitute_dcfg(apr_pool_t *p, void *basev, void *overv) apr_bucket_delete(tmp_b); \ } while (0) -static void do_pattmatch(ap_filter_t *f, apr_bucket *inb, - apr_bucket_brigade *mybb, - apr_pool_t *tmp_pool) +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; - apr_size_t fbytes; const char *buff; - const char *repl; - char *scratch; - char *p; - char *s1; - char *s2; + struct ap_varbuf vb; apr_bucket *b; apr_bucket *tmp_b; - apr_pool_t *tpool; subst_dir_conf *cfg = (subst_dir_conf *) ap_get_module_config(f->r->per_dir_config, @@ -124,11 +114,9 @@ static void do_pattmatch(ap_filter_t *f, apr_bucket *inb, subst_pattern_t *script; APR_BRIGADE_INSERT_TAIL(mybb, inb); - + ap_varbuf_init(pool, &vb, 0); + script = (subst_pattern_t *) cfg->patterns->elts; - apr_pool_create(&tpool, tmp_pool); - scratch = NULL; - fbytes = 0; /* * Simple optimization. If we only have one pattern, then * we can safely avoid the overhead of flattening @@ -149,10 +137,19 @@ static void do_pattmatch(ap_filter_t *f, apr_bucket *inb, } if (apr_bucket_read(b, &buff, &bytes, APR_BLOCK_READ) == APR_SUCCESS) { - s1 = NULL; + int have_match = 0; + vb.strlen = 0; if (script->pattern) { + const char *repl; + /* + * space_left counts how many bytes we have left until the + * line length reaches AP_SUBST_MAX_LINE_LENGTH. + */ + apr_size_t space_left = AP_SUBST_MAX_LINE_LENGTH; + apr_size_t repl_len = strlen(script->replacement); while ((repl = apr_strmatch(script->pattern, buff, bytes))) { + have_match = 1; /* get offset into buff for pattern */ len = (apr_size_t) (repl - buff); if (script->flatten && !force_quick) { @@ -164,14 +161,25 @@ static void do_pattmatch(ap_filter_t *f, apr_bucket *inb, * are constanting allocing space and copying * strings. */ - SEDSCAT(s1, s2, tmp_pool, buff, len, - script->replacement); + if (vb.strlen + len + repl_len > AP_SUBST_MAX_LINE_LENGTH) + return APR_ENOMEM; + ap_varbuf_strmemcat(&vb, buff, len); + ap_varbuf_strmemcat(&vb, script->replacement, repl_len); } else { /* - * We now split off the stuff before the regex - * as its own bucket, then isolate the pattern - * and delete it. + * 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); /* @@ -189,82 +197,104 @@ static void do_pattmatch(ap_filter_t *f, apr_bucket *inb, bytes -= len; buff += len; } - if (script->flatten && s1 && !force_quick) { - /* - * we've finished looking at the bucket, so remove the - * old one and add in our new one - */ - s2 = apr_pstrmemdup(tmp_pool, buff, bytes); - s1 = apr_pstrcat(tmp_pool, s1, s2, NULL); - tmp_b = apr_bucket_transient_create(s1, strlen(s1), - f->r->connection->bucket_alloc); - APR_BUCKET_INSERT_BEFORE(b, tmp_b); - apr_bucket_delete(b); - b = tmp_b; + 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); + 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; + } } - } else if (script->regexp) { - /* - * we need a null terminated string here :(. To hopefully - * save time and memory, we don't alloc for each run - * through, but only if we need to have a larger chunk - * to save the string to. So we keep track of how much - * we've allocated and only re-alloc when we need it. - * NOTE: this screams for a macro. - */ - if (!scratch || (bytes > (fbytes + 1))) { - fbytes = bytes + 1; - scratch = apr_palloc(tpool, fbytes); - } - /* reset pointer to the scratch space */ - p = scratch; - memcpy(p, buff, bytes); - p[bytes] = '\0'; - while (!ap_regexec(script->regexp, p, + int left = bytes; + const char *pos = buff; + char *repl; + apr_size_t space_left = AP_SUBST_MAX_LINE_LENGTH; + while (!ap_regexec_len(script->regexp, pos, left, AP_MAX_REG_MATCH, regm, 0)) { - /* first, grab the replacement string */ - repl = ap_pregsub(tmp_pool, script->replacement, p, - AP_MAX_REG_MATCH, regm); + apr_status_t rv; + have_match = 1; if (script->flatten && !force_quick) { - SEDSCAT(s1, s2, tmp_pool, p, regm[0].rm_so, repl); + /* copy bytes before the match */ + if (regm[0].rm_so > 0) + ap_varbuf_strmemcat(&vb, pos, regm[0].rm_so); + /* add replacement string */ + rv = ap_varbuf_regsub(&vb, script->replacement, pos, + AP_MAX_REG_MATCH, regm, + AP_SUBST_MAX_LINE_LENGTH - vb.strlen); + if (rv != APR_SUCCESS) + return rv; } else { + apr_size_t repl_len; + /* acount 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, - strlen(repl), - f->r->connection->bucket_alloc); + tmp_b = apr_bucket_transient_create(repl, repl_len, + f->r->connection->bucket_alloc); APR_BUCKET_INSERT_BEFORE(b, tmp_b); } /* - * reset to past what we just did. buff now maps to b + * reset to past what we just did. pos now maps to b * again */ - p += regm[0].rm_eo; + pos += regm[0].rm_eo; + left -= regm[0].rm_eo; } - if (script->flatten && s1 && !force_quick) { - s1 = apr_pstrcat(tmp_pool, s1, p, NULL); - tmp_b = apr_bucket_transient_create(s1, strlen(s1), - f->r->connection->bucket_alloc); + 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); + 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 { - /* huh? */ + ap_assert(0); continue; } } } script++; } - - apr_pool_destroy(tpool); - - return; + ap_varbuf_free(&vb); + return APR_SUCCESS; } static apr_status_t substitute_filter(ap_filter_t *f, apr_bucket_brigade *bb) @@ -281,7 +311,7 @@ static apr_status_t substitute_filter(ap_filter_t *f, apr_bucket_brigade *bb) apr_status_t rv; 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, @@ -349,9 +379,17 @@ static apr_status_t substitute_filter(ap_filter_t *f, apr_bucket_brigade *bb) if (!APR_BRIGADE_EMPTY(ctx->linebb)) { rv = apr_brigade_pflatten(ctx->linebb, &bflat, &fbytes, ctx->tpool); + if (rv != APR_SUCCESS) + goto err; + if (fbytes > AP_SUBST_MAX_LINE_LENGTH) { + rv = APR_ENOMEM; + goto err; + } tmp_b = apr_bucket_transient_create(bflat, fbytes, f->r->connection->bucket_alloc); - do_pattmatch(f, tmp_b, ctx->pattbb, ctx->tpool); + 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); @@ -407,11 +445,22 @@ static apr_status_t substitute_filter(ap_filter_t *f, apr_bucket_brigade *bb) 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 > AP_SUBST_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); } - do_pattmatch(f, b, ctx->pattbb, ctx->tpool); + 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 @@ -442,7 +491,7 @@ static apr_status_t substitute_filter(ap_filter_t *f, apr_bucket_brigade *bb) num = 0; apr_pool_clear(ctx->tpool); if (rv != APR_SUCCESS) - return rv; + goto err; } b = tmp_b; } @@ -461,10 +510,8 @@ static apr_status_t substitute_filter(ap_filter_t *f, apr_bucket_brigade *bb) if (!APR_BRIGADE_EMPTY(ctx->passbb)) { rv = ap_pass_brigade(f->next, ctx->passbb); apr_brigade_cleanup(ctx->passbb); - if (rv != APR_SUCCESS) { - apr_pool_clear(ctx->tpool); - return rv; - } + if (rv != APR_SUCCESS) + goto err; } apr_pool_clear(ctx->tpool); } @@ -482,6 +529,12 @@ static apr_status_t substitute_filter(ap_filter_t *f, apr_bucket_brigade *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) @@ -560,7 +613,7 @@ static const char *set_pattern(cmd_parms *cmd, void *cfg, const char *line) if (is_pattern) { nscript->patlen = strlen(from); - nscript->pattern = apr_strmatch_precompile(cmd->pool, from, + nscript->pattern = apr_strmatch_precompile(cmd->pool, from, !ignore_case); } else { @@ -587,7 +640,7 @@ static const command_rec substitute_cmds[] = { {NULL} }; -module AP_MODULE_DECLARE_DATA substitute_module = { +AP_DECLARE_MODULE(substitute) = { STANDARD20_MODULE_STUFF, create_substitute_dcfg, /* dir config creater */ merge_substitute_dcfg, /* dir merger --- default is to override */ diff --git a/modules/filters/mod_substitute.dep b/modules/filters/mod_substitute.dep deleted file mode 100644 index 4ae89e5b..00000000 --- a/modules/filters/mod_substitute.dep +++ /dev/null @@ -1,29 +0,0 @@ -# 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_mmn.h"\ - "..\..\include\ap_regex.h"\ - "..\..\include\ap_release.h"\ - "..\..\include\http_config.h"\ - "..\..\include\http_core.h"\ - "..\..\include\http_request.h"\ - "..\..\include\httpd.h"\ - "..\..\include\os.h"\ - "..\..\include\util_cfgtree.h"\ - "..\..\include\util_filter.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\include\apr_hash.h"\ - "..\..\srclib\apr\include\apr_lib.h"\ - "..\..\srclib\apr\include\apr_mmap.h"\ - "..\..\srclib\apr\include\apr_poll.h"\ - "..\..\srclib\apr\include\apr_strings.h"\ - diff --git a/modules/filters/mod_substitute.mak b/modules/filters/mod_substitute.mak deleted file mode 100644 index 520cdff1..00000000 --- a/modules/filters/mod_substitute.mak +++ /dev/null @@ -1,353 +0,0 @@ -# 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 00000000..fd544444 --- /dev/null +++ b/modules/filters/mod_xml2enc.c @@ -0,0 +1,628 @@ +/* Copyright (c) 2007-11, WebThing Ltd + * Copyright (c) 2011-, The Apache Software Foundation + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined(WIN32) +#define XML2ENC_DECLARE_EXPORT +#endif + +#include <ctype.h> + +/* libxml2 */ +#include <libxml/encoding.h> + +#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 ans 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_REMOVE(b); + apr_bucket_destroy(b); + } + ctx->bytes -= (p-ctx->buf); + ctx->buf = p ; + found = 1; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01428) + "Skipped to first <%s> element", + starts[i].val) ; + break; + } + } + p = ap_strchr(p+1, '<'); + } + if (p == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01429) + "Failed to find start of recognised HTML!"); + } + } +} +static void sniff_encoding(request_rec* r, xml2ctx* ctx) +{ + xml2cfg* cfg = NULL; /* initialise to shut compiler warnings up */ + char* p ; + apr_bucket* cutb; + apr_bucket* cute; + apr_bucket* b; + ap_regmatch_t match[2] ; + apr_status_t rv; + const char* ctype = r->content_type; + + if (ctype) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01430) + "Content-Type is %s", ctype) ; + + /* If we've got it in the HTTP headers, there's nothing to do */ + if (ctype && (p = ap_strcasestr(ctype, "charset=") , p != NULL)) { + p += 8 ; + if (ctx->encoding = apr_pstrndup(r->pool, p, strcspn(p, " ;") ), + ctx->encoding) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01431) + "Got charset %s from HTTP headers", ctx->encoding) ; + ctx->xml2enc = xmlParseCharEncoding(ctx->encoding); + } + } + } + + /* to sniff, first we look for BOM */ + if (ctx->xml2enc == XML_CHAR_ENCODING_NONE) { + ctx->xml2enc = xmlDetectCharEncoding((const xmlChar*)ctx->buf, + ctx->bytes); + if (HAVE_ENCODING(ctx->xml2enc)) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01432) + "Got charset from XML rules.") ; + ctx->encoding = xmlGetCharEncodingName(ctx->xml2enc); + } + } + + /* If none of the above, look for a META-thingey */ + /* also we're probably about to invalidate it, so we remove it. */ + if (ap_regexec(seek_meta_ctype, ctx->buf, 1, match, 0) == 0 ) { + /* get markers on the start and end of the match */ + rv = apr_brigade_partition(ctx->bbsave, match[0].rm_eo, &cute); + ap_assert(rv == APR_SUCCESS); + rv = apr_brigade_partition(ctx->bbsave, match[0].rm_so, &cutb); + ap_assert(rv == APR_SUCCESS); + /* now set length of useful buf for start-of-data hooks */ + ctx->bytes = match[0].rm_so; + if (ctx->encoding == NULL) { + p = apr_pstrndup(r->pool, ctx->buf + match[0].rm_so, + match[0].rm_eo - match[0].rm_so) ; + if (ap_regexec(seek_charset, p, 2, match, 0) == 0) { + if (ctx->encoding = apr_pstrndup(r->pool, p+match[1].rm_so, + match[1].rm_eo - match[1].rm_so), + ctx->encoding) { + ctx->xml2enc = xmlParseCharEncoding(ctx->encoding); + if (HAVE_ENCODING(ctx->xml2enc)) + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01433) + "Got charset %s from HTML META", ctx->encoding) ; + } + } + } + + /* cut out the <meta> we're invalidating */ + while (cutb != cute) { + b = APR_BUCKET_NEXT(cutb); + APR_BUCKET_REMOVE(cutb); + apr_bucket_destroy(cutb); + cutb = b; + } + /* and leave a string */ + ctx->buf[ctx->bytes] = 0; + } + + /* either it's set to something we found or it's still the default */ + /* Aaargh! libxml2 has undocumented <META-crap> support. So this fails + * if metafix is not active. Have to make it conditional. + * + * No, that means no-metafix breaks things. Deal immediately with + * this particular instance of metafix. + */ + if (!HAVE_ENCODING(ctx->xml2enc)) { + cfg = ap_get_module_config(r->per_dir_config, &xml2enc_module); + if (!ctx->encoding) { + ctx->encoding = cfg->default_charset?cfg->default_charset:"ISO-8859-1"; + } + /* Unsupported charset. Can we get (iconv) support through apr_xlate? */ + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01434) + "Charset %s not supported by libxml2; trying apr_xlate", + ctx->encoding); + if (apr_xlate_open(&ctx->convset, "UTF-8", ctx->encoding, r->pool) + == APR_SUCCESS) { + ctx->xml2enc = XML_CHAR_ENCODING_UTF8 ; + } else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01435) + "Charset %s not supported. Consider aliasing it?", + ctx->encoding) ; + } + } + + if (!HAVE_ENCODING(ctx->xml2enc)) { + /* Use configuration default as a last resort */ + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01436) + "No usable charset information; using configuration default"); + ctx->xml2enc = (cfg->default_encoding == XML_CHAR_ENCODING_NONE) + ? XML_CHAR_ENCODING_8859_1 : cfg->default_encoding ; + } + if (ctype && ctx->encoding) { + if (ap_regexec(seek_charset, ctype, 2, match, 0)) { + r->content_type = apr_pstrcat(r->pool, ctype, ";charset=utf-8", + NULL); + } else { + char* str = apr_palloc(r->pool, strlen(r->content_type) + 13 + - (match[0].rm_eo - match[0].rm_so) + 1); + memcpy(str, r->content_type, match[1].rm_so); + memcpy(str + match[1].rm_so, "utf-8", 5); + strcpy(str + match[1].rm_so + 5, r->content_type+match[1].rm_eo); + r->content_type = str; + } + } +} + +static apr_status_t xml2enc_filter_init(ap_filter_t* f) +{ + xml2ctx* ctx; + if (!f->ctx) { + xml2cfg* cfg = ap_get_module_config(f->r->per_dir_config, + &xml2enc_module); + f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(xml2ctx)); + ctx->xml2enc = XML_CHAR_ENCODING_NONE; + if (cfg->skipto != NULL) { + ctx->flags |= ENC_SKIPTO; + } + } + return APR_SUCCESS; +} +static apr_status_t xml2enc_ffunc(ap_filter_t* f, apr_bucket_brigade* bb) +{ + xml2ctx* ctx = f->ctx; + apr_status_t rv; + apr_bucket* b; + apr_bucket* bstart; + apr_size_t insz = 0; + 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); + + /* consume the data we just sniffed */ + /* we need to omit any <meta> we just invalidated */ + ctx->flags |= ENC_INITIALISED; + ap_set_module_config(f->r->request_config, &xml2enc_module, ctx); + } + if (ctx->bbnext == NULL) { + ctx->bbnext = apr_brigade_create(f->r->pool, + f->r->connection->bucket_alloc); + } + + if (!ctx->convset) { + rv = ap_pass_brigade(f->next, ctx->bbsave); + apr_brigade_cleanup(ctx->bbsave); + ap_remove_output_filter(f); + return rv; + } + /* move the data back to bb */ + APR_BRIGADE_CONCAT(bb, ctx->bbsave); + + while (b = APR_BRIGADE_FIRST(bb), b != APR_BRIGADE_SENTINEL(bb)) { + ctx->bytes = 0; + if (APR_BUCKET_IS_METADATA(b)) { + if (APR_BUCKET_IS_EOS(b)) { + /* send remaining data */ + return ap_fflush(f->next, ctx->bbnext); + } else if (APR_BUCKET_IS_FLUSH(b)) { + ap_fflush(f->next, ctx->bbnext); + } + APR_BUCKET_REMOVE(b); + apr_bucket_destroy(b); + } + else { /* 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_REMOVE(b); + apr_bucket_destroy(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"); + else + rv = ap_pass_brigade(f->next, 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; + } + } + return APR_SUCCESS; +} +static apr_status_t xml2enc_charset(request_rec* r, xmlCharEncoding* encp, + const char** encoding) +{ + xml2ctx* ctx = ap_get_module_config(r->request_config, &xml2enc_module); + if (!ctx || !(ctx->flags & ENC_INITIALISED)) { + return APR_EAGAIN; + } + *encp = ctx->xml2enc; + *encoding = ctx->encoding; + return HAVE_ENCODING(ctx->xml2enc) ? APR_SUCCESS : APR_EGENERAL; +} + +#define PROTO_FLAGS AP_FILTER_PROTO_CHANGE|AP_FILTER_PROTO_CHANGE_LENGTH +static void xml2enc_hooks(apr_pool_t* pool) +{ + ap_register_output_filter_protocol("xml2enc", xml2enc_ffunc, + xml2enc_filter_init, + AP_FTYPE_RESOURCE, PROTO_FLAGS); + APR_REGISTER_OPTIONAL_FN(xml2enc_filter); + APR_REGISTER_OPTIONAL_FN(xml2enc_charset); + seek_meta_ctype = ap_pregcomp(pool, + "(<meta[^>]*http-equiv[ \t\r\n='\"]*content-type[^>]*>)", + AP_REG_EXTENDED|AP_REG_ICASE) ; + seek_charset = ap_pregcomp(pool, "charset=([A-Za-z0-9_-]+)", + AP_REG_EXTENDED|AP_REG_ICASE) ; +} +static const char* set_alias(cmd_parms* cmd, void* CFG, + const char* charset, const char* alias) +{ + const char* errmsg = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (errmsg != NULL) + return errmsg ; + else if (xmlAddEncodingAlias(charset, alias) == 0) + return NULL; + else + return "Error setting charset alias"; +} + +static const char* set_default(cmd_parms* cmd, void* CFG, const char* charset) +{ + xml2cfg* cfg = CFG; + cfg->default_charset = charset; + cfg->default_encoding = xmlParseCharEncoding(charset); + switch(cfg->default_encoding) { + case XML_CHAR_ENCODING_NONE: + return "Default charset not found"; + case XML_CHAR_ENCODING_ERROR: + return "Invalid or unsupported default charset"; + default: + return NULL; + } +} +static const char* set_skipto(cmd_parms* cmd, void* CFG, const char* arg) +{ + tattr* attr; + xml2cfg* cfg = CFG; + if (cfg->skipto == NULL) + cfg->skipto = apr_array_make(cmd->pool, 4, sizeof(tattr)); + attr = apr_array_push(cfg->skipto) ; + attr->val = arg; + return NULL; +} + +static const command_rec xml2enc_cmds[] = { + AP_INIT_TAKE1("xml2EncDefault", set_default, NULL, OR_ALL, + "Usage: xml2EncDefault charset"), + AP_INIT_ITERATE2("xml2EncAlias", set_alias, NULL, RSRC_CONF, + "EncodingAlias charset alias [more aliases]"), + AP_INIT_ITERATE("xml2StartParse", set_skipto, NULL, OR_ALL, + "Ignore anything in front of the first of these elements"), + { NULL } +}; +static void* xml2enc_config(apr_pool_t* pool, char* x) +{ + xml2cfg* ret = apr_pcalloc(pool, sizeof(xml2cfg)); + ret->default_encoding = XML_CHAR_ENCODING_NONE ; + return ret; +} + +static void* xml2enc_merge(apr_pool_t* pool, void* BASE, void* ADD) +{ + xml2cfg* base = BASE; + xml2cfg* add = ADD; + xml2cfg* ret = apr_pcalloc(pool, sizeof(xml2cfg)); + ret->default_encoding = (add->default_encoding == XML_CHAR_ENCODING_NONE) + ? base->default_encoding : add->default_encoding ; + ret->default_charset = add->default_charset + ? add->default_charset : base->default_charset; + ret->skipto = add->skipto ? add->skipto : base->skipto; + return ret; +} +module AP_MODULE_DECLARE_DATA xml2enc_module = { + 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.dsp b/modules/filters/mod_xml2enc.dsp new file mode 100644 index 00000000..6ad6ef3a --- /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" +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 /i /d "_DEBUG" "../../include" /i "../../srclib/apr/include" /i "../../srclib/apr-util/include" /i "../../srclib/libxml2/include" +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 00000000..90d0e0fa --- /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/regexp.c b/modules/filters/regexp.c new file mode 100644 index 00000000..ba118a65 --- /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" +#ifdef APR_HAVE_LIMITS_H +#include <limits.h> +#endif +#if APR_HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include "libsed.h" +#include "regexp.h" +#include "sed.h" + +#define GETC() ((unsigned char)*sp++) +#define PEEKC() ((unsigned char)*sp) +#define UNGETC(c) (--sp) +#define SEDCOMPILE_ERROR(c) { \ + regerrno = c; \ + goto out; \ + } +#define ecmp(s1, s2, n) (strncmp(s1, s2, n) == 0) +#define uletter(c) (isalpha(c) || c == '_') + + +static unsigned char bittab[] = { 1, 2, 4, 8, 16, 32, 64, 128 }; + +static int regerr(sed_commands_t *commands, int err); +static void comperr(sed_commands_t *commands, char *msg); +static void getrnge(char *str, step_vars_storage *vars); +static int _advance(char *, char *, step_vars_storage *); +extern int sed_step(char *p1, char *p2, int circf, step_vars_storage *vars); + + +static void comperr(sed_commands_t *commands, char *msg) +{ + command_errf(commands, msg, commands->linebuf); +} + +/* +*/ +static int regerr(sed_commands_t *commands, int err) +{ + switch(err) { + case 0: + /* No error */ + break; + case 11: + comperr(commands, "Range endpoint too large: %s"); + break; + + case 16: + comperr(commands, "Bad number: %s"); + break; + + case 25: + comperr(commands, "``\\digit'' out of range: %s"); + break; + + case 36: + comperr(commands, "Illegal or missing delimiter: %s"); + break; + + case 41: + comperr(commands, "No remembered search string: %s"); + break; + + case 42: + comperr(commands, "\\( \\) imbalance: %s"); + break; + + case 43: + comperr(commands, "Too many \\(: %s"); + break; + + case 44: + comperr(commands, "More than 2 numbers given in \\{ \\}: %s"); + break; + + case 45: + comperr(commands, "} expected after \\: %s"); + break; + + case 46: + comperr(commands, "First number exceeds second in \\{ \\}: %s"); + break; + + case 49: + comperr(commands, "[ ] imbalance: %s"); + break; + + case 50: + comperr(commands, SEDERR_TMMES); + break; + + default: + comperr(commands, "Unknown regexp error code %s\n"); + break; + } + return (0); +} + + +char *sed_compile(sed_commands_t *commands, sed_comp_args *compargs, + char *ep, char *endbuf, int seof) +{ + int c; + int eof = seof; + char *lastep; + int cclcnt; + char bracket[NBRA], *bracketp; + int closed; + int neg; + int lc; + int i, cflg; + int iflag; /* used for non-ascii characters in brackets */ + char *sp = commands->cp; + int regerrno = 0; + + lastep = 0; + if ((c = GETC()) == eof || c == '\n') { + if (c == '\n') { + UNGETC(c); + } + commands->cp = sp; + goto out; + } + bracketp = bracket; + compargs->circf = closed = compargs->nbra = 0; + if (c == '^') + compargs->circf++; + else + UNGETC(c); + while (1) { + if (ep >= endbuf) + SEDCOMPILE_ERROR(50); + c = GETC(); + if (c != '*' && ((c != '\\') || (PEEKC() != '{'))) + lastep = ep; + if (c == eof) { + *ep++ = CCEOF; + if (bracketp != bracket) + SEDCOMPILE_ERROR(42); + commands->cp = sp; + goto out; + } + switch (c) { + + case '.': + *ep++ = CDOT; + continue; + + case '\n': + SEDCOMPILE_ERROR(36); + commands->cp = sp; + goto out; + case '*': + if (lastep == 0 || *lastep == CBRA || *lastep == CKET) + goto defchar; + *lastep |= STAR; + continue; + + case '$': + if (PEEKC() != eof && PEEKC() != '\n') + goto defchar; + *ep++ = CDOL; + continue; + + case '[': + if (&ep[17] >= endbuf) + SEDCOMPILE_ERROR(50); + + *ep++ = CCL; + lc = 0; + for (i = 0; i < 16; i++) + ep[i] = 0; + + neg = 0; + if ((c = GETC()) == '^') { + neg = 1; + c = GETC(); + } + iflag = 1; + do { + c &= 0377; + if (c == '\0' || c == '\n') + SEDCOMPILE_ERROR(49); + if ((c & 0200) && iflag) { + iflag = 0; + if (&ep[32] >= endbuf) + SEDCOMPILE_ERROR(50); + ep[-1] = CXCL; + for (i = 16; i < 32; i++) + ep[i] = 0; + } + if (c == '-' && lc != 0) { + if ((c = GETC()) == ']') { + PLACE('-'); + break; + } + if ((c & 0200) && iflag) { + iflag = 0; + if (&ep[32] >= endbuf) + SEDCOMPILE_ERROR(50); + ep[-1] = CXCL; + for (i = 16; i < 32; i++) + ep[i] = 0; + } + while (lc < c) { + PLACE(lc); + lc++; + } + } + lc = c; + PLACE(c); + } while ((c = GETC()) != ']'); + + if (iflag) + iflag = 16; + else + iflag = 32; + + if (neg) { + if (iflag == 32) { + for (cclcnt = 0; cclcnt < iflag; + cclcnt++) + ep[cclcnt] ^= 0377; + ep[0] &= 0376; + } else { + ep[-1] = NCCL; + /* make nulls match so test fails */ + ep[0] |= 01; + } + } + + ep += iflag; + + continue; + + case '\\': + switch (c = GETC()) { + + case '(': + if (compargs->nbra >= NBRA) + SEDCOMPILE_ERROR(43); + *bracketp++ = compargs->nbra; + *ep++ = CBRA; + *ep++ = compargs->nbra++; + continue; + + case ')': + if (bracketp <= bracket) + SEDCOMPILE_ERROR(42); + *ep++ = CKET; + *ep++ = *--bracketp; + closed++; + continue; + + case '{': + if (lastep == (char *) 0) + goto defchar; + *lastep |= RNGE; + cflg = 0; + nlim: + c = GETC(); + i = 0; + do { + if ('0' <= c && c <= '9') + i = 10 * i + c - '0'; + else + SEDCOMPILE_ERROR(16); + } while (((c = GETC()) != '\\') && (c != ',')); + if (i >= 255) + SEDCOMPILE_ERROR(11); + *ep++ = i; + if (c == ',') { + if (cflg++) + SEDCOMPILE_ERROR(44); + if ((c = GETC()) == '\\') + *ep++ = (char) 255; + else { + UNGETC(c); + goto nlim; + /* get 2'nd number */ + } + } + if (GETC() != '}') + SEDCOMPILE_ERROR(45); + if (!cflg) /* one number */ + *ep++ = i; + else if ((ep[-1] & 0377) < (ep[-2] & 0377)) + SEDCOMPILE_ERROR(46); + continue; + + case '\n': + SEDCOMPILE_ERROR(36); + + case 'n': + c = '\n'; + goto defchar; + + default: + if (c >= '1' && c <= '9') { + if ((c -= '1') >= closed) + SEDCOMPILE_ERROR(25); + *ep++ = CBACK; + *ep++ = c; + continue; + } + } + /* Drop through to default to use \ to turn off special chars */ + + defchar: + default: + lastep = ep; + *ep++ = CCHR; + *ep++ = c; + } + } +out: + if (regerrno) { + regerr(commands, regerrno); + return (char*) NULL; + } + /* XXX : Basant : what extra */ + /* int reglength = (int)(ep - expbuf); */ + return ep; +} + +int sed_step(char *p1, char *p2, int circf, step_vars_storage *vars) +{ + int c; + + + if (circf) { + vars->loc1 = p1; + return (_advance(p1, p2, vars)); + } + /* fast check for first character */ + if (*p2 == CCHR) { + c = p2[1]; + do { + if (*p1 != c) + continue; + if (_advance(p1, p2, vars)) { + vars->loc1 = p1; + return (1); + } + } while (*p1++); + return (0); + } + /* regular algorithm */ + do { + if (_advance(p1, p2, vars)) { + vars->loc1 = p1; + return (1); + } + } while (*p1++); + return (0); +} + +static int _advance(char *lp, char *ep, step_vars_storage *vars) +{ + char *curlp; + int c; + char *bbeg; + char neg; + int ct; + int epint; /* int value of *ep */ + + while (1) { + neg = 0; + switch (*ep++) { + + case CCHR: + if (*ep++ == *lp++) + continue; + return (0); + + case CDOT: + if (*lp++) + continue; + return (0); + + case CDOL: + if (*lp == 0) + continue; + return (0); + + case CCEOF: + vars->loc2 = lp; + return (1); + + case CXCL: + c = (unsigned char)*lp++; + if (ISTHERE(c)) { + ep += 32; + continue; + } + return (0); + + case NCCL: + neg = 1; + + case CCL: + c = *lp++; + if (((c & 0200) == 0 && ISTHERE(c)) ^ neg) { + ep += 16; + continue; + } + return (0); + + case CBRA: + epint = (int) *ep; + vars->braslist[epint] = lp; + ep++; + continue; + + case CKET: + epint = (int) *ep; + vars->braelist[epint] = lp; + ep++; + continue; + + case CCHR | RNGE: + c = *ep++; + getrnge(ep, vars); + while (vars->low--) + if (*lp++ != c) + return (0); + curlp = lp; + while (vars->size--) + if (*lp++ != c) + break; + if (vars->size < 0) + lp++; + ep += 2; + goto star; + + case CDOT | RNGE: + getrnge(ep, vars); + while (vars->low--) + if (*lp++ == '\0') + return (0); + curlp = lp; + while (vars->size--) + if (*lp++ == '\0') + break; + if (vars->size < 0) + lp++; + ep += 2; + goto star; + + case CXCL | RNGE: + getrnge(ep + 32, vars); + while (vars->low--) { + c = (unsigned char)*lp++; + if (!ISTHERE(c)) + return (0); + } + curlp = lp; + while (vars->size--) { + c = (unsigned char)*lp++; + if (!ISTHERE(c)) + break; + } + if (vars->size < 0) + lp++; + ep += 34; /* 32 + 2 */ + goto star; + + case NCCL | RNGE: + neg = 1; + + case CCL | RNGE: + getrnge(ep + 16, vars); + while (vars->low--) { + c = *lp++; + if (((c & 0200) || !ISTHERE(c)) ^ neg) + return (0); + } + curlp = lp; + while (vars->size--) { + c = *lp++; + if (((c & 0200) || !ISTHERE(c)) ^ neg) + break; + } + if (vars->size < 0) + lp++; + ep += 18; /* 16 + 2 */ + goto star; + + case CBACK: + epint = (int) *ep; + bbeg = vars->braslist[epint]; + ct = vars->braelist[epint] - bbeg; + ep++; + + if (ecmp(bbeg, lp, ct)) { + lp += ct; + continue; + } + return (0); + + case CBACK | STAR: + epint = (int) *ep; + bbeg = vars->braslist[epint]; + ct = vars->braelist[epint] - bbeg; + ep++; + curlp = lp; + while (ecmp(bbeg, lp, ct)) + lp += ct; + + while (lp >= curlp) { + if (_advance(lp, ep, vars)) + return (1); + lp -= ct; + } + return (0); + + + case CDOT | STAR: + curlp = lp; + while (*lp++); + goto star; + + case CCHR | STAR: + curlp = lp; + while (*lp++ == *ep); + ep++; + goto star; + + case CXCL | STAR: + curlp = lp; + do { + c = (unsigned char)*lp++; + } while (ISTHERE(c)); + ep += 32; + goto star; + + case NCCL | STAR: + neg = 1; + + case CCL | STAR: + curlp = lp; + do { + c = *lp++; + } while (((c & 0200) == 0 && ISTHERE(c)) ^ neg); + ep += 16; + goto star; + + star: + do { + if (--lp == vars->locs) + break; + if (_advance(lp, ep, vars)) + return (1); + } while (lp > curlp); + return (0); + + } + } +} + +static void getrnge(char *str, step_vars_storage *vars) +{ + vars->low = *str++ & 0377; + vars->size = ((*str & 0377) == 255)? 20000: (*str &0377) - vars->low; +} + + diff --git a/modules/filters/regexp.h b/modules/filters/regexp.h new file mode 100644 index 00000000..1e5a6269 --- /dev/null +++ b/modules/filters/regexp.h @@ -0,0 +1,111 @@ +/* + * 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, ...); + +#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 %lld" +#define SEDERR_TMRMES "too many reads after line %lld" +#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 00000000..cc3c03eb --- /dev/null +++ b/modules/filters/sed.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2005, 2008 Sun Microsystems, Inc. All Rights Reserved. + * Use is subject to license terms. + * + * Copyright (c) 1984 AT&T + * All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _SED_H +#define _SED_H + +#include <stdlib.h> +#include <limits.h> + +#define CEND 16 +#define CLNUM 14 + +#define RESIZE 10000 +#define LBSIZE 1000 + +#define ACOM 01 +#define BCOM 020 +#define CCOM 02 +#define CDCOM 025 +#define CNCOM 022 +#define COCOM 017 +#define CPCOM 023 +#define DCOM 03 +#define ECOM 015 +#define EQCOM 013 +#define FCOM 016 +#define GCOM 027 +#define CGCOM 030 +#define HCOM 031 +#define CHCOM 032 +#define ICOM 04 +#define LCOM 05 +#define NCOM 012 +#define PCOM 010 +#define QCOM 011 +#define RCOM 06 +#define SCOM 07 +#define TCOM 021 +#define WCOM 014 +#define CWCOM 024 +#define YCOM 026 +#define XCOM 033 + +#endif /* _SED_H */ diff --git a/modules/filters/sed0.c b/modules/filters/sed0.c new file mode 100644 index 00000000..da47122a --- /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); + 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]) == 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]) == 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, commands->linebuf); + 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, commands->linebuf); + 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, commands->linebuf); + 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, commands->linebuf); + *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 00000000..ddca3bdb --- /dev/null +++ b/modules/filters/sed1.c @@ -0,0 +1,1018 @@ +/* + * 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" + +char *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" +}; +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, int 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 + +/* + * grow_buffer + */ +static void grow_buffer(apr_pool_t *pool, char **buffer, + char **spend, unsigned int *cursize, + unsigned int newsize) +{ + char* newbuffer = NULL; + int spendsize = 0; + if (*cursize >= newsize) + return; + /* 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); + 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; + } +} + +/* + * grow_line_buffer + */ +static void grow_line_buffer(sed_eval_t *eval, int newsize) +{ + grow_buffer(eval->pool, &eval->linebuf, &eval->lspend, + &eval->lsize, newsize); +} + +/* + * grow_hold_buffer + */ +static void grow_hold_buffer(sed_eval_t *eval, int newsize) +{ + grow_buffer(eval->pool, &eval->holdbuf, &eval->hspend, + &eval->hsize, newsize); +} + +/* + * grow_gen_buffer + */ +static void grow_gen_buffer(sed_eval_t *eval, int newsize, + char **gspend) +{ + if (gspend == NULL) { + gspend = &eval->genbuf; + } + grow_buffer(eval->pool, &eval->genbuf, gspend, + &eval->gsize, newsize); + eval->lcomend = &eval->genbuf[71]; +} + +/* + * appendmem_to_linebuf + */ +static void appendmem_to_linebuf(sed_eval_t *eval, const char* sz, int len) +{ + unsigned int reqsize = (eval->lspend - eval->linebuf) + len; + if (eval->lsize < reqsize) { + grow_line_buffer(eval, reqsize); + } + memcpy(eval->lspend, sz, len); + eval->lspend += len; +} + +/* + * append_to_linebuf + */ +static void append_to_linebuf(sed_eval_t *eval, const char* sz) +{ + int len = strlen(sz); + /* Copy string including null character */ + appendmem_to_linebuf(eval, sz, len + 1); + --eval->lspend; /* lspend will now point to NULL character */ +} + +/* + * copy_to_linebuf + */ +static void copy_to_linebuf(sed_eval_t *eval, const char* sz) +{ + eval->lspend = eval->linebuf; + append_to_linebuf(eval, sz); +} + +/* + * append_to_holdbuf + */ +static void append_to_holdbuf(sed_eval_t *eval, const char* sz) +{ + int len = strlen(sz); + unsigned int reqsize = (eval->hspend - eval->holdbuf) + len + 1; + if (eval->hsize <= reqsize) { + grow_hold_buffer(eval, reqsize); + } + strcpy(eval->hspend, sz); + /* hspend will now point to NULL character */ + eval->hspend += len; +} + +/* + * copy_to_holdbuf + */ +static void copy_to_holdbuf(sed_eval_t *eval, const char* sz) +{ + eval->hspend = eval->holdbuf; + append_to_holdbuf(eval, sz); +} + +/* + * append_to_genbuf + */ +static void append_to_genbuf(sed_eval_t *eval, const char* sz, char **gspend) +{ + int len = strlen(sz); + unsigned int reqsize = (*gspend - eval->genbuf) + len + 1; + if (eval->gsize < reqsize) { + grow_gen_buffer(eval, reqsize, gspend); + } + strcpy(*gspend, sz); + /* *gspend will now point to NULL character */ + *gspend += len; +} + +/* + * copy_to_genbuf + */ +static void copy_to_genbuf(sed_eval_t *eval, const char* sz) +{ + int len = strlen(sz); + unsigned int reqsize = len + 1; + if (eval->gsize < reqsize) { + grow_gen_buffer(eval, reqsize, NULL); + } +} + +/* + * 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, int 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) { + char *n; + int 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; + } + + appendmem_to_linebuf(eval, buf, llen + 1); + --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) { + appendmem_to_linebuf(eval, buf, bufsz); + } + + 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; + + 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 */ + append_to_linebuf(eval, ""); + } + + *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); + 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 */ + grow_gen_buffer(eval, eval->gsize + 1024, &sp); + } + } + lp = step_vars->loc2; + step_vars->loc2 = sp - eval->genbuf + eval->linebuf; + append_to_genbuf(eval, lp, &sp); + copy_to_linebuf(eval, eval->genbuf); + return rv; +} + +/* + * place + */ +static char *place(sed_eval_t *eval, char *asp, char *al1, char *al2) +{ + char *sp = asp; + int n = al2 - al1; + unsigned int reqsize = (sp - eval->genbuf) + n + 1; + + if (eval->gsize < reqsize) { + grow_gen_buffer(eval, reqsize, &sp); + } + 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, *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++; + copy_to_linebuf(eval, p1); + eval->jflag++; + break; + + case EQCOM: + length = apr_snprintf(sz, sizeof(sz), "%d", (int) eval->lnum); + rv = wline(eval, sz, length); + break; + + case GCOM: + copy_to_linebuf(eval, eval->holdbuf); + break; + + case CGCOM: + append_to_linebuf(eval, "\n"); + append_to_linebuf(eval, eval->holdbuf); + break; + + case HCOM: + copy_to_holdbuf(eval, eval->linebuf); + break; + + case CHCOM: + append_to_holdbuf(eval, "\n"); + append_to_holdbuf(eval, eval->linebuf); + 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; + } + append_to_linebuf(eval, "\n"); + 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: + copy_to_genbuf(eval, eval->linebuf); + copy_to_linebuf(eval, eval->holdbuf); + copy_to_holdbuf(eval, eval->genbuf); + 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, int 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; +} + |