diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:01:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:01:30 +0000 |
commit | 6beeb1b708550be0d4a53b272283e17e5e35fe17 (patch) | |
tree | 1ce8673d4aaa948e5554000101f46536a1e4cc29 /modules/mappers | |
parent | Initial commit. (diff) | |
download | apache2-upstream.tar.xz apache2-upstream.zip |
Adding upstream version 2.4.57.upstream/2.4.57upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
56 files changed, 18845 insertions, 0 deletions
diff --git a/modules/mappers/.indent.pro b/modules/mappers/.indent.pro new file mode 100644 index 0000000..a9fbe9f --- /dev/null +++ b/modules/mappers/.indent.pro @@ -0,0 +1,54 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-Tbuf_area +-Tbuff_struct +-Tbuffy +-Tcmd_how +-Tcmd_parms +-Tcommand_rec +-Tcommand_struct +-Tconn_rec +-Tcore_dir_config +-Tcore_server_config +-Tdir_maker_func +-Tevent +-Tglobals_s +-Thandler_func +-Thandler_rec +-Tjoblist_s +-Tlisten_rec +-Tmerger_func +-Tmode_t +-Tmodule +-Tmodule_struct +-Tmutex +-Tn_long +-Tother_child_rec +-Toverrides_t +-Tparent_score +-Tpid_t +-Tpiped_log +-Tpool +-Trequest_rec +-Trequire_line +-Trlim_t +-Tscoreboard +-Tsemaphore +-Tserver_addr_rec +-Tserver_rec +-Tserver_rec_chain +-Tshort_score +-Ttable +-Ttable_entry +-Tthread +-Tu_wide_int +-Tvtime_t +-Twide_int diff --git a/modules/mappers/Makefile.in b/modules/mappers/Makefile.in new file mode 100644 index 0000000..167b343 --- /dev/null +++ b/modules/mappers/Makefile.in @@ -0,0 +1,3 @@ + +include $(top_srcdir)/build/special.mk + diff --git a/modules/mappers/NWGNUactions b/modules/mappers/NWGNUactions new file mode 100644 index 0000000..3a94773 --- /dev/null +++ b/modules/mappers/NWGNUactions @@ -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 = actions + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Actions Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Actions 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)/actions.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_actions.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 = \ + actions_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/mappers/NWGNUimagemap b/modules/mappers/NWGNUimagemap new file mode 100644 index 0000000..7f66086 --- /dev/null +++ b/modules/mappers/NWGNUimagemap @@ -0,0 +1,249 @@ +# +# 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 \ + $(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 += \ + $(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 = imagemap + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Image Map Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Image Map 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)/imagemap.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_imagemap.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 = \ + imagemap_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/mappers/NWGNUmakefile b/modules/mappers/NWGNUmakefile new file mode 100644 index 0000000..d7abda5 --- /dev/null +++ b/modules/mappers/NWGNUmakefile @@ -0,0 +1,250 @@ +# +# Declare the sub-directories to be built here +# + +SUBDIRS = \ + $(EOLIST) + +# +# Get the 'head' of the build environment. This includes default targets and +# paths to tools +# + +include $(AP_WORK)/build/NWGNUhead.inc + +# +# build this level's files + +# +# Make sure all needed macro's are defined +# + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/actions.nlm \ + $(OBJDIR)/imagemap.nlm \ + $(OBJDIR)/rewrite.nlm \ + $(OBJDIR)/speling.nlm \ + $(OBJDIR)/userdir.nlm \ + $(OBJDIR)/vhost.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 = \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + $(call COPY,$(OBJDIR)/*.nlm, $(INSTALLBASE)/modules/) + +# +# Any specialized rules here +# + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/mappers/NWGNUrewrite b/modules/mappers/NWGNUrewrite new file mode 100644 index 0000000..d16dd8e --- /dev/null +++ b/modules/mappers/NWGNUrewrite @@ -0,0 +1,250 @@ +# +# 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 \ + $(AP_WORK)/modules/database \ + $(AP_WORK)/modules/ssl \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = rewrite + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Rewrite Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Rewrite 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)/rewrite.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_rewrite.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 = \ + rewrite_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/mappers/NWGNUspeling b/modules/mappers/NWGNUspeling new file mode 100644 index 0000000..6982988 --- /dev/null +++ b/modules/mappers/NWGNUspeling @@ -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 = speling + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Speling Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Speling 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)/speling.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_speling.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 = \ + speling_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/mappers/NWGNUuserdir b/modules/mappers/NWGNUuserdir new file mode 100644 index 0000000..b0fc7a0 --- /dev/null +++ b/modules/mappers/NWGNUuserdir @@ -0,0 +1,249 @@ +# +# 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 \ + $(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 += \ + $(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 = userdir + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) User Dir Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = UserDir 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)/userdir.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_userdir.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 = \ + userdir_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/mappers/NWGNUvhost b/modules/mappers/NWGNUvhost new file mode 100644 index 0000000..d3b655c --- /dev/null +++ b/modules/mappers/NWGNUvhost @@ -0,0 +1,249 @@ +# +# 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 \ + $(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 += \ + $(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 = vhost + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Vhost Alias Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Vhost Alias 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)/vhost.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_vhost_alias.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 = \ + vhost_alias_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/mappers/config9.m4 b/modules/mappers/config9.m4 new file mode 100644 index 0000000..55a97ab --- /dev/null +++ b/modules/mappers/config9.m4 @@ -0,0 +1,19 @@ +dnl modules enabled in this directory by default + +dnl APACHE_MODULE(name, helptext[, objects[, structname[, default[, config]]]]) + +APACHE_MODPATH_INIT(mappers) + +APACHE_MODULE(vhost_alias, mass virtual hosting module, , , most) +APACHE_MODULE(negotiation, content negotiation, , , most) +APACHE_MODULE(dir, directory request handling, , , yes) +APACHE_MODULE(imagemap, server-side imagemaps, , , no) +APACHE_MODULE(actions, Action triggering on requests, , , most) +APACHE_MODULE(speling, correct common URL misspellings, , , most) +APACHE_MODULE(userdir, mapping of requests to user-specific directories, , , most) +APACHE_MODULE(alias, mapping of requests to different filesystem parts, , , yes) +APACHE_MODULE(rewrite, rule based URL manipulation, , , most) + +APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/$modpath_current]) + +APACHE_MODPATH_FINISH diff --git a/modules/mappers/mod_actions.c b/modules/mappers/mod_actions.c new file mode 100644 index 0000000..ac9c3b7 --- /dev/null +++ b/modules/mappers/mod_actions.c @@ -0,0 +1,230 @@ +/* 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_actions.c: executes scripts based on MIME type or HTTP method + * + * by Alexei Kosut; based on mod_cgi.c, mod_mime.c and mod_includes.c, + * adapted by rst from original NCSA code by Rob McCool + * + * Usage instructions: + * + * Action mime/type /cgi-bin/script + * + * will activate /cgi-bin/script when a file of content type mime/type is + * requested. It sends the URL and file path of the requested document using + * the standard CGI PATH_INFO and PATH_TRANSLATED environment variables. + * + * Script PUT /cgi-bin/script + * + * will activate /cgi-bin/script when a request is received with the + * HTTP method "PUT". The available method names are defined in httpd.h. + * If the method is GET, the script will only be activated if the requested + * URI includes query information (stuff after a ?-mark). + */ + +#include "apr_strings.h" +#define APR_WANT_STRFUNC +#include "apr_want.h" +#include "ap_config.h" +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_main.h" +#include "http_log.h" +#include "util_script.h" + +typedef struct { + apr_table_t *action_types; /* Added with Action... */ + const char *scripted[METHODS]; /* Added with Script... */ + int configured; /* True if Action or Script has been + * called at least once + */ +} action_dir_config; + +module AP_MODULE_DECLARE_DATA actions_module; + +static void *create_action_dir_config(apr_pool_t *p, char *dummy) +{ + action_dir_config *new = + (action_dir_config *) apr_pcalloc(p, sizeof(action_dir_config)); + + new->action_types = apr_table_make(p, 4); + + return new; +} + +static void *merge_action_dir_configs(apr_pool_t *p, void *basev, void *addv) +{ + action_dir_config *base = (action_dir_config *) basev; + action_dir_config *add = (action_dir_config *) addv; + action_dir_config *new = (action_dir_config *) apr_palloc(p, + sizeof(action_dir_config)); + int i; + + new->action_types = apr_table_overlay(p, add->action_types, + base->action_types); + + for (i = 0; i < METHODS; ++i) { + new->scripted[i] = add->scripted[i] ? add->scripted[i] + : base->scripted[i]; + } + + new->configured = (base->configured || add->configured); + return new; +} + +static const char *add_action(cmd_parms *cmd, void *m_v, + const char *type, const char *script, + const char *option) +{ + action_dir_config *m = (action_dir_config *)m_v; + + if (option && strcasecmp(option, "virtual")) { + return apr_pstrcat(cmd->pool, + "unrecognized option '", option, "'", NULL); + } + + apr_table_setn(m->action_types, type, + apr_pstrcat(cmd->pool, option ? "1" : "0", script, NULL)); + m->configured = 1; + + return NULL; +} + +static const char *set_script(cmd_parms *cmd, void *m_v, + const char *method, const char *script) +{ + action_dir_config *m = (action_dir_config *)m_v; + int methnum; + if (cmd->pool == cmd->temp_pool) { + /* In .htaccess, we can't globally register new methods. */ + methnum = ap_method_number_of(method); + } + else { + /* ap_method_register recognizes already registered methods, + * so don't bother to check its previous existence explicitly. + */ + methnum = ap_method_register(cmd->pool, method); + } + + if (methnum == M_TRACE) { + return "TRACE not allowed for Script"; + } + else if (methnum == M_INVALID) { + return apr_pstrcat(cmd->pool, "Could not register method '", method, + "' for Script", NULL); + } + + m->scripted[methnum] = script; + m->configured = 1; + + return NULL; +} + +static const command_rec action_cmds[] = +{ + AP_INIT_TAKE23("Action", add_action, NULL, OR_FILEINFO, + "a media type followed by a script name"), + AP_INIT_TAKE2("Script", set_script, NULL, ACCESS_CONF | RSRC_CONF, + "a method followed by a script name"), + {NULL} +}; + +static int action_handler(request_rec *r) +{ + action_dir_config *conf = (action_dir_config *) + ap_get_module_config(r->per_dir_config, &actions_module); + const char *t, *action; + const char *script; + int i; + + if (!conf->configured) { + return DECLINED; + } + + /* Note that this handler handles _all_ types, so handler is unchecked */ + + /* Set allowed stuff */ + for (i = 0; i < METHODS; ++i) { + if (conf->scripted[i]) + r->allowed |= (AP_METHOD_BIT << i); + } + + /* First, check for the method-handling scripts */ + if (r->method_number == M_GET) { + if (r->args) + script = conf->scripted[M_GET]; + else + script = NULL; + } + else { + script = conf->scripted[r->method_number]; + } + + /* Check for looping, which can happen if the CGI script isn't */ + if (script && r->prev && r->prev->prev) + return DECLINED; + + /* Second, check for actions (which override the method scripts) */ + action = r->handler ? r->handler : + ap_field_noparam(r->pool, r->content_type); + + if (action && (t = apr_table_get(conf->action_types, action))) { + int virtual = (*t++ == '0' ? 0 : 1); + if (!virtual && r->finfo.filetype == APR_NOFILE) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00652) + "File does not exist: %s", r->filename); + return HTTP_NOT_FOUND; + } + + script = t; + /* propagate the handler name to the script + * (will be REDIRECT_HANDLER there) + */ + apr_table_setn(r->subprocess_env, "HANDLER", action); + if (virtual) { + apr_table_setn(r->notes, "virtual_script", "1"); + } + } + + if (script == NULL) + return DECLINED; + + ap_internal_redirect_handler(apr_pstrcat(r->pool, script, + ap_escape_uri(r->pool, r->uri), + r->args ? "?" : NULL, + r->args, NULL), r); + return OK; +} + +static void register_hooks(apr_pool_t *p) +{ + ap_hook_handler(action_handler,NULL,NULL,APR_HOOK_LAST); +} + +AP_DECLARE_MODULE(actions) = +{ + STANDARD20_MODULE_STUFF, + create_action_dir_config, /* dir config creater */ + merge_action_dir_configs, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + action_cmds, /* command apr_table_t */ + register_hooks /* register hooks */ +}; diff --git a/modules/mappers/mod_actions.dep b/modules/mappers/mod_actions.dep new file mode 100644 index 0000000..9bfdaf1 --- /dev/null +++ b/modules/mappers/mod_actions.dep @@ -0,0 +1,58 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_actions.mak + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + + +.\mod_actions.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_script.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + diff --git a/modules/mappers/mod_actions.dsp b/modules/mappers/mod_actions.dsp new file mode 100644 index 0000000..736937c --- /dev/null +++ b/modules/mappers/mod_actions.dsp @@ -0,0 +1,111 @@ +# Microsoft Developer Studio Project File - Name="mod_actions" - 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_actions - 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_actions.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_actions.mak" CFG="mod_actions - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_actions - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_actions - 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_actions - 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_actions_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_actions.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_actions.so" /d LONG_NAME="actions_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_actions.so" /base:@..\..\os\win32\BaseAddr.ref,mod_actions.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_actions.so" /base:@..\..\os\win32\BaseAddr.ref,mod_actions.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_actions.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_actions - 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_actions_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_actions.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_actions.so" /d LONG_NAME="actions_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_actions.so" /base:@..\..\os\win32\BaseAddr.ref,mod_actions.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_actions.so" /base:@..\..\os\win32\BaseAddr.ref,mod_actions.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_actions.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_actions - Win32 Release" +# Name "mod_actions - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_actions.c +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/mappers/mod_actions.exp b/modules/mappers/mod_actions.exp new file mode 100644 index 0000000..8cc6cdb --- /dev/null +++ b/modules/mappers/mod_actions.exp @@ -0,0 +1 @@ +actions_module diff --git a/modules/mappers/mod_actions.mak b/modules/mappers/mod_actions.mak new file mode 100644 index 0000000..28ec1e0 --- /dev/null +++ b/modules/mappers/mod_actions.mak @@ -0,0 +1,353 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_actions.dsp +!IF "$(CFG)" == "" +CFG=mod_actions - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_actions - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_actions - Win32 Release" && "$(CFG)" != "mod_actions - 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_actions.mak" CFG="mod_actions - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_actions - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_actions - 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_actions - 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_actions.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_actions.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_actions.obj" + -@erase "$(INTDIR)\mod_actions.res" + -@erase "$(INTDIR)\mod_actions_src.idb" + -@erase "$(INTDIR)\mod_actions_src.pdb" + -@erase "$(OUTDIR)\mod_actions.exp" + -@erase "$(OUTDIR)\mod_actions.lib" + -@erase "$(OUTDIR)\mod_actions.pdb" + -@erase "$(OUTDIR)\mod_actions.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_actions_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_actions.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_actions.so" /d LONG_NAME="actions_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_actions.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_actions.pdb" /debug /out:"$(OUTDIR)\mod_actions.so" /implib:"$(OUTDIR)\mod_actions.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_actions.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_actions.obj" \ + "$(INTDIR)\mod_actions.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" + +"$(OUTDIR)\mod_actions.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_actions.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_actions.so" + if exist .\Release\mod_actions.so.manifest mt.exe -manifest .\Release\mod_actions.so.manifest -outputresource:.\Release\mod_actions.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_actions - 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_actions.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_actions.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_actions.obj" + -@erase "$(INTDIR)\mod_actions.res" + -@erase "$(INTDIR)\mod_actions_src.idb" + -@erase "$(INTDIR)\mod_actions_src.pdb" + -@erase "$(OUTDIR)\mod_actions.exp" + -@erase "$(OUTDIR)\mod_actions.lib" + -@erase "$(OUTDIR)\mod_actions.pdb" + -@erase "$(OUTDIR)\mod_actions.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_actions_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_actions.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_actions.so" /d LONG_NAME="actions_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_actions.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_actions.pdb" /debug /out:"$(OUTDIR)\mod_actions.so" /implib:"$(OUTDIR)\mod_actions.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_actions.so +LINK32_OBJS= \ + "$(INTDIR)\mod_actions.obj" \ + "$(INTDIR)\mod_actions.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" + +"$(OUTDIR)\mod_actions.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_actions.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_actions.so" + if exist .\Debug\mod_actions.so.manifest mt.exe -manifest .\Debug\mod_actions.so.manifest -outputresource:.\Debug\mod_actions.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_actions.dep") +!INCLUDE "mod_actions.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_actions.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_actions - Win32 Release" || "$(CFG)" == "mod_actions - Win32 Debug" + +!IF "$(CFG)" == "mod_actions - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\mappers" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_actions - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\mappers" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ENDIF + +!IF "$(CFG)" == "mod_actions - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\mappers" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_actions - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\mappers" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ENDIF + +!IF "$(CFG)" == "mod_actions - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\mappers" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_actions - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\mappers" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\mappers" + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_actions - Win32 Release" + + +"$(INTDIR)\mod_actions.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_actions.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_actions.so" /d LONG_NAME="actions_module for Apache" $(SOURCE) + + +!ELSEIF "$(CFG)" == "mod_actions - Win32 Debug" + + +"$(INTDIR)\mod_actions.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_actions.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_actions.so" /d LONG_NAME="actions_module for Apache" $(SOURCE) + + +!ENDIF + +SOURCE=.\mod_actions.c + +"$(INTDIR)\mod_actions.obj" : $(SOURCE) "$(INTDIR)" + + + +!ENDIF + diff --git a/modules/mappers/mod_alias.c b/modules/mappers/mod_alias.c new file mode 100644 index 0000000..5ff937b --- /dev/null +++ b/modules/mappers/mod_alias.c @@ -0,0 +1,727 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * http_alias.c: Stuff for dealing with directory aliases + * + * Original by Rob McCool, rewritten in succession by David Robinson + * and rst. + * + */ + +#include "apr_strings.h" +#include "apr_lib.h" + +#define APR_WANT_STRFUNC +#include "apr_want.h" + +#include "ap_config.h" +#include "httpd.h" +#include "http_core.h" +#include "http_config.h" +#include "http_request.h" +#include "http_log.h" +#include "ap_expr.h" + + +typedef struct { + const char *real; + const char *fake; + char *handler; + ap_regex_t *regexp; + int redir_status; /* 301, 302, 303, 410, etc */ +} alias_entry; + +typedef struct { + apr_array_header_t *aliases; + apr_array_header_t *redirects; +} alias_server_conf; + +typedef struct { + unsigned int alias_set:1; + unsigned int redirect_set:1; + apr_array_header_t *redirects; + const ap_expr_info_t *alias; + char *handler; + const ap_expr_info_t *redirect; + int redirect_status; /* 301, 302, 303, 410, etc */ +} alias_dir_conf; + +module AP_MODULE_DECLARE_DATA alias_module; + +static char magic_error_value; +#define PREGSUB_ERROR (&magic_error_value) + +static void *create_alias_config(apr_pool_t *p, server_rec *s) +{ + alias_server_conf *a = + (alias_server_conf *) apr_pcalloc(p, sizeof(alias_server_conf)); + + a->aliases = apr_array_make(p, 20, sizeof(alias_entry)); + a->redirects = apr_array_make(p, 20, sizeof(alias_entry)); + return a; +} + +static void *create_alias_dir_config(apr_pool_t *p, char *d) +{ + alias_dir_conf *a = + (alias_dir_conf *) apr_pcalloc(p, sizeof(alias_dir_conf)); + a->redirects = apr_array_make(p, 2, sizeof(alias_entry)); + return a; +} + +static void *merge_alias_config(apr_pool_t *p, void *basev, void *overridesv) +{ + alias_server_conf *a = + (alias_server_conf *) apr_pcalloc(p, sizeof(alias_server_conf)); + alias_server_conf *base = (alias_server_conf *) basev; + alias_server_conf *overrides = (alias_server_conf *) overridesv; + + a->aliases = apr_array_append(p, overrides->aliases, base->aliases); + a->redirects = apr_array_append(p, overrides->redirects, base->redirects); + return a; +} + +static void *merge_alias_dir_config(apr_pool_t *p, void *basev, void *overridesv) +{ + alias_dir_conf *a = + (alias_dir_conf *) apr_pcalloc(p, sizeof(alias_dir_conf)); + alias_dir_conf *base = (alias_dir_conf *) basev; + alias_dir_conf *overrides = (alias_dir_conf *) overridesv; + + a->redirects = apr_array_append(p, overrides->redirects, base->redirects); + + a->alias = (overrides->alias_set == 0) ? base->alias : overrides->alias; + a->handler = (overrides->alias_set == 0) ? base->handler : overrides->handler; + a->alias_set = overrides->alias_set || base->alias_set; + + a->redirect = (overrides->redirect_set == 0) ? base->redirect : overrides->redirect; + a->redirect_status = (overrides->redirect_set == 0) ? base->redirect_status : overrides->redirect_status; + a->redirect_set = overrides->redirect_set || base->redirect_set; + + return a; +} + +/* need prototype for overlap check */ +static int alias_matches(const char *uri, const char *alias_fakename); + +static const char *add_alias_internal(cmd_parms *cmd, void *dummy, + const char *fake, const char *real, + int use_regex) +{ + server_rec *s = cmd->server; + alias_server_conf *conf = ap_get_module_config(s->module_config, + &alias_module); + alias_entry *new = apr_array_push(conf->aliases); + alias_entry *entries = (alias_entry *)conf->aliases->elts; + int i; + + /* XXX: real can NOT be relative to DocumentRoot here... compat bug. */ + + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_CONTEXT); + + if (err != NULL) { + return err; + } + + if (use_regex) { + new->regexp = ap_pregcomp(cmd->pool, fake, AP_REG_EXTENDED); + if (new->regexp == NULL) + return "Regular expression could not be compiled."; + new->real = real; + } + else { + /* XXX This may be optimized, but we must know that new->real + * exists. If so, we can dir merge later, trusing new->real + * and just canonicalizing the remainder. Not till I finish + * cleaning out the old ap_canonical stuff first. + */ + new->real = real; + } + new->fake = fake; + new->handler = cmd->info; + + /* check for overlapping (Script)Alias directives + * and throw a warning if found one + */ + if (!use_regex) { + for (i = 0; i < conf->aliases->nelts - 1; ++i) { + alias_entry *alias = &entries[i]; + + if ( (!alias->regexp && alias_matches(fake, alias->fake) > 0) + || (alias->regexp && !ap_regexec(alias->regexp, fake, 0, NULL, 0))) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00671) + "The %s directive in %s at line %d will probably " + "never match because it overlaps an earlier " + "%sAlias%s.", + cmd->cmd->name, cmd->directive->filename, + cmd->directive->line_num, + alias->handler ? "Script" : "", + alias->regexp ? "Match" : ""); + + break; /* one warning per alias should be sufficient */ + } + } + } + + return NULL; +} + +static const char *add_alias(cmd_parms *cmd, void *dummy, const char *fake, + const char *real) +{ + if (real) { + + return add_alias_internal(cmd, dummy, fake, real, 0); + + } + else { + alias_dir_conf *dirconf = (alias_dir_conf *) dummy; + + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES); + + if (err != NULL) { + return err; + } + + if (!cmd->path) { + return "Alias must have two arguments when used globally"; + } + + dirconf->alias = + ap_expr_parse_cmd(cmd, fake, AP_EXPR_FLAG_STRING_RESULT, + &err, NULL); + if (err) { + return apr_pstrcat(cmd->temp_pool, + "Cannot parse alias expression '", fake, "': ", err, + NULL); + } + + dirconf->handler = cmd->info; + dirconf->alias_set = 1; + + return NULL; + + } +} + +static const char *add_alias_regex(cmd_parms *cmd, void *dummy, + const char *fake, const char *real) +{ + return add_alias_internal(cmd, dummy, fake, real, 1); +} + +static const char *add_redirect_internal(cmd_parms *cmd, + alias_dir_conf *dirconf, + const char *arg1, const char *arg2, + const char *arg3, int use_regex) +{ + alias_entry *new; + server_rec *s = cmd->server; + alias_server_conf *serverconf = ap_get_module_config(s->module_config, + &alias_module); + int status = (int) (long) cmd->info; + int grokarg1 = 1; + ap_regex_t *regex = NULL; + const char *fake = arg2; + const char *url = arg3; + + /* + * Logic flow: + * Go ahead and try to grok the 1st arg, in case it is a + * Redirect status. Now if we have 3 args, we expect that + * we were able to understand that 1st argument (it's something + * we expected, so if not, then we bail + */ + if (!strcasecmp(arg1, "permanent")) + status = HTTP_MOVED_PERMANENTLY; + else if (!strcasecmp(arg1, "temp")) + status = HTTP_MOVED_TEMPORARILY; + else if (!strcasecmp(arg1, "seeother")) + status = HTTP_SEE_OTHER; + else if (!strcasecmp(arg1, "gone")) { + status = HTTP_GONE; + grokarg1 = -1; + } + else if (apr_isdigit(*arg1)) { + status = atoi(arg1); + if (!ap_is_HTTP_REDIRECT(status)) { + grokarg1 = -1; + } + } + else { + grokarg1 = 0; + } + + if (arg3 && !grokarg1) + return "Redirect: invalid first argument (of three)"; + + /* + * if we have the 2nd arg and we understand the 1st one as a redirect + * status (3xx, but not things like 404 /robots.txt), or if we have the + * 1st arg but don't understand it, we use the expression syntax assuming + * a path from the location. + * + * if we understand the first arg but have no second arg, we are dealing + * with a status like "GONE" or a non-redirect status (e.g. 404, 503). + */ + if (!cmd->path) { + /* <Location> context only for now */ + ; + } + else if ((grokarg1 > 0 && arg2 && !arg3) || (!grokarg1 && !arg2)) { + const char *expr_err = NULL; + + url = grokarg1 ? arg2 : arg1; + dirconf->redirect = + ap_expr_parse_cmd(cmd, url, AP_EXPR_FLAG_STRING_RESULT, + &expr_err, NULL); + if (expr_err) { + return apr_pstrcat(cmd->temp_pool, + "Cannot parse redirect expression '", url, "': ", expr_err, + NULL); + } + + dirconf->redirect_status = status; + dirconf->redirect_set = 1; + + return NULL; + + } + else if (grokarg1 < 0 && !arg2) { + + dirconf->redirect_status = status; + dirconf->redirect_set = 1; + + return NULL; + + } + + /* + * if we don't have the 3rd arg and we didn't understand the 1st + * one, then assume URL-path URL. This also handles case, eg, GONE + * we even though we don't have a 3rd arg, we did understand the 1st + * one, so we don't want to re-arrange + */ + if (!arg3 && !grokarg1) { + fake = arg1; + url = arg2; + } + + if (use_regex) { + regex = ap_pregcomp(cmd->pool, fake, AP_REG_EXTENDED); + if (regex == NULL) + return "Regular expression could not be compiled."; + } + + if (ap_is_HTTP_REDIRECT(status)) { + if (!url) + return "URL to redirect to is missing"; + /* PR#35314: we can allow path components here; + * they get correctly resolved to full URLs. + */ + if (!use_regex && !ap_is_url(url) && (url[0] != '/')) + return "Redirect to non-URL"; + } + else { + if (url) + return "Redirect URL not valid for this status"; + } + + if (cmd->path) + new = apr_array_push(dirconf->redirects); + else + new = apr_array_push(serverconf->redirects); + + new->fake = fake; + new->real = url; + new->regexp = regex; + new->redir_status = status; + return NULL; +} + +static const char *add_redirect(cmd_parms *cmd, void *dirconf, + const char *arg1, const char *arg2, + const char *arg3) +{ + return add_redirect_internal(cmd, dirconf, arg1, arg2, arg3, 0); +} + +static const char *add_redirect2(cmd_parms *cmd, void *dirconf, + const char *arg1, const char *arg2) +{ + return add_redirect_internal(cmd, dirconf, arg1, arg2, NULL, 0); +} + +static const char *add_redirect_regex(cmd_parms *cmd, void *dirconf, + const char *arg1, const char *arg2, + const char *arg3) +{ + return add_redirect_internal(cmd, dirconf, arg1, arg2, arg3, 1); +} + +static int alias_matches(const char *uri, const char *alias_fakename) +{ + const char *aliasp = alias_fakename, *urip = uri; + + while (*aliasp) { + if (*aliasp == '/') { + /* any number of '/' in the alias matches any number in + * the supplied URI, but there must be at least one... + */ + if (*urip != '/') + return 0; + + do { + ++aliasp; + } while (*aliasp == '/'); + do { + ++urip; + } while (*urip == '/'); + } + else { + /* Other characters are compared literally */ + if (*urip++ != *aliasp++) + return 0; + } + } + + /* Check last alias path component matched all the way */ + + if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/') + return 0; + + /* Return number of characters from URI which matched (may be + * greater than length of alias, since we may have matched + * doubled slashes) + */ + + return urip - uri; +} + +static char *try_alias(request_rec *r) +{ + alias_dir_conf *dirconf = + (alias_dir_conf *) ap_get_module_config(r->per_dir_config, &alias_module); + + if (dirconf->alias) { + const char *err = NULL; + + char *found = apr_pstrdup(r->pool, + ap_expr_str_exec(r, dirconf->alias, &err)); + if (err) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02825) + "Can't evaluate alias expression: %s", err); + return PREGSUB_ERROR; + } + + if (dirconf->handler) { /* Set handler, and leave a note for mod_cgi */ + r->handler = dirconf->handler; + apr_table_setn(r->notes, "alias-forced-type", r->handler); + } + /* XXX This is as SLOW as can be, next step, we optimize + * and merge to whatever part of the found path was already + * canonicalized. After I finish eliminating os canonical. + * Better fail test for ap_server_root_relative needed here. + */ + found = ap_server_root_relative(r->pool, found); + return found; + + } + + return NULL; +} + +static char *try_redirect(request_rec *r, int *status) +{ + alias_dir_conf *dirconf = + (alias_dir_conf *) ap_get_module_config(r->per_dir_config, &alias_module); + + if (dirconf->redirect_set) { + apr_uri_t uri; + const char *err = NULL; + char *found = ""; + + if (dirconf->redirect) { + + found = apr_pstrdup(r->pool, + ap_expr_str_exec(r, dirconf->redirect, &err)); + if (err) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02826) + "Can't evaluate redirect expression: %s", err); + return PREGSUB_ERROR; + } + + apr_uri_parse(r->pool, found, &uri); + /* Do not escape the query string or fragment. */ + found = apr_uri_unparse(r->pool, &uri, APR_URI_UNP_OMITQUERY); + found = ap_escape_uri(r->pool, found); + if (uri.query) { + found = apr_pstrcat(r->pool, found, "?", uri.query, NULL); + } + if (uri.fragment) { + found = apr_pstrcat(r->pool, found, "#", uri.fragment, NULL); + } + + } + + *status = dirconf->redirect_status; + return found; + + } + + return NULL; +} + +static char *try_alias_list(request_rec *r, apr_array_header_t *aliases, + int is_redir, int *status) +{ + alias_entry *entries = (alias_entry *) aliases->elts; + ap_regmatch_t regm[AP_MAX_REG_MATCH]; + char *found = NULL; + int i; + + for (i = 0; i < aliases->nelts; ++i) { + alias_entry *alias = &entries[i]; + int l; + + if (alias->regexp) { + if (!ap_regexec(alias->regexp, r->uri, AP_MAX_REG_MATCH, regm, 0)) { + if (alias->real) { + found = ap_pregsub(r->pool, alias->real, r->uri, + AP_MAX_REG_MATCH, regm); + if (found) { + if (is_redir) { + apr_uri_t uri; + apr_uri_parse(r->pool, found, &uri); + /* Do not escape the query string or fragment. */ + found = apr_uri_unparse(r->pool, &uri, + APR_URI_UNP_OMITQUERY); + found = ap_escape_uri(r->pool, found); + if (uri.query) { + found = apr_pstrcat(r->pool, found, "?", + uri.query, NULL); + } + if (uri.fragment) { + found = apr_pstrcat(r->pool, found, "#", + uri.fragment, NULL); + } + } + } + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00672) + "Regex substitution in '%s' failed. " + "Replacement too long?", alias->real); + return PREGSUB_ERROR; + } + } + else { + /* need something non-null */ + found = ""; + } + } + } + else { + l = alias_matches(r->uri, alias->fake); + + if (l > 0) { + ap_set_context_info(r, alias->fake, alias->real); + if (is_redir) { + char *escurl; + escurl = ap_os_escape_path(r->pool, r->uri + l, 1); + + found = apr_pstrcat(r->pool, alias->real, escurl, NULL); + } + else + found = apr_pstrcat(r->pool, alias->real, r->uri + l, NULL); + } + } + + if (found) { + if (alias->handler) { /* Set handler, and leave a note for mod_cgi */ + r->handler = alias->handler; + apr_table_setn(r->notes, "alias-forced-type", r->handler); + } + /* XXX This is as SLOW as can be, next step, we optimize + * and merge to whatever part of the found path was already + * canonicalized. After I finish eliminating os canonical. + * Better fail test for ap_server_root_relative needed here. + */ + if (!is_redir) { + found = ap_server_root_relative(r->pool, found); + } + if (found) { + *status = alias->redir_status; + } + return found; + } + + } + + return NULL; +} + +static int translate_alias_redir(request_rec *r) +{ + ap_conf_vector_t *sconf = r->server->module_config; + alias_server_conf *serverconf = ap_get_module_config(sconf, &alias_module); + char *ret; + int status; + + if (r->uri[0] != '/' && r->uri[0] != '\0') { + return DECLINED; + } + + if ((ret = try_redirect(r, &status)) != NULL + || (ret = try_alias_list(r, serverconf->redirects, 1, &status)) + != NULL) { + if (ret == PREGSUB_ERROR) + return HTTP_INTERNAL_SERVER_ERROR; + if (ap_is_HTTP_REDIRECT(status)) { + if (ret[0] == '/') { + char *orig_target = ret; + + ret = ap_construct_url(r->pool, ret, r); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00673) + "incomplete redirection target of '%s' for " + "URI '%s' modified to '%s'", + orig_target, r->uri, ret); + } + if (!ap_is_url(ret)) { + status = HTTP_INTERNAL_SERVER_ERROR; + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00674) + "cannot redirect '%s' to '%s'; " + "target is not a valid absoluteURI or abs_path", + r->uri, ret); + } + else { + /* append requested query only, if the config didn't + * supply its own. + */ + if (r->args && !ap_strchr(ret, '?')) { + ret = apr_pstrcat(r->pool, ret, "?", r->args, NULL); + } + apr_table_setn(r->headers_out, "Location", ret); + } + } + return status; + } + + if ((ret = try_alias(r)) != NULL + || (ret = try_alias_list(r, serverconf->aliases, 0, &status)) + != NULL) { + r->filename = ret; + return OK; + } + + return DECLINED; +} + +static int fixup_redir(request_rec *r) +{ + void *dconf = r->per_dir_config; + alias_dir_conf *dirconf = + (alias_dir_conf *) ap_get_module_config(dconf, &alias_module); + char *ret; + int status; + + /* It may have changed since last time, so try again */ + + if ((ret = try_redirect(r, &status)) != NULL + || (ret = try_alias_list(r, dirconf->redirects, 1, &status)) + != NULL) { + if (ret == PREGSUB_ERROR) + return HTTP_INTERNAL_SERVER_ERROR; + if (ap_is_HTTP_REDIRECT(status)) { + if (ret[0] == '/') { + char *orig_target = ret; + + ret = ap_construct_url(r->pool, ret, r); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00675) + "incomplete redirection target of '%s' for " + "URI '%s' modified to '%s'", + orig_target, r->uri, ret); + } + if (!ap_is_url(ret)) { + status = HTTP_INTERNAL_SERVER_ERROR; + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00676) + "cannot redirect '%s' to '%s'; " + "target is not a valid absoluteURI or abs_path", + r->uri, ret); + } + else { + /* append requested query only, if the config didn't + * supply its own. + */ + if (r->args && !ap_strchr(ret, '?')) { + ret = apr_pstrcat(r->pool, ret, "?", r->args, NULL); + } + apr_table_setn(r->headers_out, "Location", ret); + } + } + return status; + } + + return DECLINED; +} + +static const command_rec alias_cmds[] = +{ + AP_INIT_TAKE12("Alias", add_alias, NULL, RSRC_CONF | ACCESS_CONF, + "a fakename and a realname, or a realname in a Location"), + AP_INIT_TAKE12("ScriptAlias", add_alias, "cgi-script", RSRC_CONF | ACCESS_CONF, + "a fakename and a realname, or a realname in a Location"), + AP_INIT_TAKE123("Redirect", add_redirect, (void *) HTTP_MOVED_TEMPORARILY, + OR_FILEINFO, + "an optional status, then document to be redirected and " + "destination URL"), + AP_INIT_TAKE2("AliasMatch", add_alias_regex, NULL, RSRC_CONF, + "a regular expression and a filename"), + AP_INIT_TAKE2("ScriptAliasMatch", add_alias_regex, "cgi-script", RSRC_CONF, + "a regular expression and a filename"), + AP_INIT_TAKE23("RedirectMatch", add_redirect_regex, + (void *) HTTP_MOVED_TEMPORARILY, OR_FILEINFO, + "an optional status, then a regular expression and " + "destination URL"), + AP_INIT_TAKE2("RedirectTemp", add_redirect2, + (void *) HTTP_MOVED_TEMPORARILY, OR_FILEINFO, + "a document to be redirected, then the destination URL"), + AP_INIT_TAKE2("RedirectPermanent", add_redirect2, + (void *) HTTP_MOVED_PERMANENTLY, OR_FILEINFO, + "a document to be redirected, then the destination URL"), + {NULL} +}; + + +static void register_hooks(apr_pool_t *p) +{ + static const char * const aszSucc[]={ "mod_userdir.c", + "mod_vhost_alias.c",NULL }; + + ap_hook_translate_name(translate_alias_redir,NULL,aszSucc,APR_HOOK_MIDDLE); + ap_hook_fixups(fixup_redir,NULL,NULL,APR_HOOK_MIDDLE); +} + +AP_DECLARE_MODULE(alias) = +{ + STANDARD20_MODULE_STUFF, + create_alias_dir_config, /* dir config creater */ + merge_alias_dir_config, /* dir merger --- default is to override */ + create_alias_config, /* server config */ + merge_alias_config, /* merge server configs */ + alias_cmds, /* command apr_table_t */ + register_hooks /* register hooks */ +}; diff --git a/modules/mappers/mod_alias.dep b/modules/mappers/mod_alias.dep new file mode 100644 index 0000000..fab6805 --- /dev/null +++ b/modules/mappers/mod_alias.dep @@ -0,0 +1,51 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_alias.mak + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + + +.\mod_alias.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_request.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_filter.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + diff --git a/modules/mappers/mod_alias.dsp b/modules/mappers/mod_alias.dsp new file mode 100644 index 0000000..4c8cf84 --- /dev/null +++ b/modules/mappers/mod_alias.dsp @@ -0,0 +1,111 @@ +# Microsoft Developer Studio Project File - Name="mod_alias" - 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_alias - 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_alias.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_alias.mak" CFG="mod_alias - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_alias - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_alias - 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_alias - 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_alias_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_alias.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_alias.so" /d LONG_NAME="alias_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_alias.so" /base:@..\..\os\win32\BaseAddr.ref,mod_alias.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_alias.so" /base:@..\..\os\win32\BaseAddr.ref,mod_alias.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_alias.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_alias - 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_alias_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_alias.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_alias.so" /d LONG_NAME="alias_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_alias.so" /base:@..\..\os\win32\BaseAddr.ref,mod_alias.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_alias.so" /base:@..\..\os\win32\BaseAddr.ref,mod_alias.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_alias.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_alias - Win32 Release" +# Name "mod_alias - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_alias.c +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/mappers/mod_alias.exp b/modules/mappers/mod_alias.exp new file mode 100644 index 0000000..ac386ec --- /dev/null +++ b/modules/mappers/mod_alias.exp @@ -0,0 +1 @@ +alias_module diff --git a/modules/mappers/mod_alias.mak b/modules/mappers/mod_alias.mak new file mode 100644 index 0000000..17ad406 --- /dev/null +++ b/modules/mappers/mod_alias.mak @@ -0,0 +1,353 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_alias.dsp +!IF "$(CFG)" == "" +CFG=mod_alias - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_alias - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_alias - Win32 Release" && "$(CFG)" != "mod_alias - 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_alias.mak" CFG="mod_alias - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_alias - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_alias - 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_alias - 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_alias.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_alias.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_alias.obj" + -@erase "$(INTDIR)\mod_alias.res" + -@erase "$(INTDIR)\mod_alias_src.idb" + -@erase "$(INTDIR)\mod_alias_src.pdb" + -@erase "$(OUTDIR)\mod_alias.exp" + -@erase "$(OUTDIR)\mod_alias.lib" + -@erase "$(OUTDIR)\mod_alias.pdb" + -@erase "$(OUTDIR)\mod_alias.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_alias_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_alias.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_alias.so" /d LONG_NAME="alias_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_alias.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_alias.pdb" /debug /out:"$(OUTDIR)\mod_alias.so" /implib:"$(OUTDIR)\mod_alias.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_alias.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_alias.obj" \ + "$(INTDIR)\mod_alias.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" + +"$(OUTDIR)\mod_alias.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_alias.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_alias.so" + if exist .\Release\mod_alias.so.manifest mt.exe -manifest .\Release\mod_alias.so.manifest -outputresource:.\Release\mod_alias.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_alias - 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_alias.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_alias.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_alias.obj" + -@erase "$(INTDIR)\mod_alias.res" + -@erase "$(INTDIR)\mod_alias_src.idb" + -@erase "$(INTDIR)\mod_alias_src.pdb" + -@erase "$(OUTDIR)\mod_alias.exp" + -@erase "$(OUTDIR)\mod_alias.lib" + -@erase "$(OUTDIR)\mod_alias.pdb" + -@erase "$(OUTDIR)\mod_alias.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_alias_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_alias.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_alias.so" /d LONG_NAME="alias_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_alias.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_alias.pdb" /debug /out:"$(OUTDIR)\mod_alias.so" /implib:"$(OUTDIR)\mod_alias.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_alias.so +LINK32_OBJS= \ + "$(INTDIR)\mod_alias.obj" \ + "$(INTDIR)\mod_alias.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" + +"$(OUTDIR)\mod_alias.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_alias.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_alias.so" + if exist .\Debug\mod_alias.so.manifest mt.exe -manifest .\Debug\mod_alias.so.manifest -outputresource:.\Debug\mod_alias.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_alias.dep") +!INCLUDE "mod_alias.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_alias.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_alias - Win32 Release" || "$(CFG)" == "mod_alias - Win32 Debug" + +!IF "$(CFG)" == "mod_alias - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\mappers" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_alias - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\mappers" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ENDIF + +!IF "$(CFG)" == "mod_alias - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\mappers" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_alias - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\mappers" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ENDIF + +!IF "$(CFG)" == "mod_alias - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\mappers" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_alias - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\mappers" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\mappers" + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_alias - Win32 Release" + + +"$(INTDIR)\mod_alias.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_alias.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_alias.so" /d LONG_NAME="alias_module for Apache" $(SOURCE) + + +!ELSEIF "$(CFG)" == "mod_alias - Win32 Debug" + + +"$(INTDIR)\mod_alias.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_alias.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_alias.so" /d LONG_NAME="alias_module for Apache" $(SOURCE) + + +!ENDIF + +SOURCE=.\mod_alias.c + +"$(INTDIR)\mod_alias.obj" : $(SOURCE) "$(INTDIR)" + + + +!ENDIF + diff --git a/modules/mappers/mod_dir.c b/modules/mappers/mod_dir.c new file mode 100644 index 0000000..3e30198 --- /dev/null +++ b/modules/mappers/mod_dir.c @@ -0,0 +1,417 @@ +/* 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_dir.c: handle default index files, and trailing-/ redirects + */ + +#include "apr_strings.h" +#include "apr_lib.h" +#include "ap_config.h" +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_request.h" +#include "http_protocol.h" +#include "http_log.h" +#include "http_main.h" +#include "util_script.h" +#include "mod_rewrite.h" + +module AP_MODULE_DECLARE_DATA dir_module; + +typedef enum { + MODDIR_OFF = 0, + MODDIR_ON, + MODDIR_UNSET +} moddir_cfg; + +#define REDIRECT_OFF 0 +#define REDIRECT_UNSET 1 + +typedef struct dir_config_struct { + apr_array_header_t *index_names; + moddir_cfg do_slash; + moddir_cfg checkhandler; + int redirect_index; + const char *dflt; +} dir_config_rec; + +#define DIR_CMD_PERMS OR_INDEXES + +static const char *add_index(cmd_parms *cmd, void *dummy, const char *arg) +{ + dir_config_rec *d = dummy; + const char *t, *w; + int count = 0; + + if (!d->index_names) { + d->index_names = apr_array_make(cmd->pool, 2, sizeof(char *)); + } + + t = arg; + while ((w = ap_getword_conf(cmd->pool, &t)) && w[0]) { + if (count == 0 && !strcasecmp(w, "disabled")) { + /* peek to see if "disabled" is first in a series of arguments */ + const char *tt = t; + const char *ww = ap_getword_conf(cmd->temp_pool, &tt); + if (ww[0] == '\0') { + /* "disabled" is first, and alone */ + apr_array_clear(d->index_names); + break; + } + } + *(const char **)apr_array_push(d->index_names) = w; + count++; + } + + return NULL; +} + +static const char *configure_slash(cmd_parms *cmd, void *d_, int arg) +{ + dir_config_rec *d = d_; + + d->do_slash = arg ? MODDIR_ON : MODDIR_OFF; + return NULL; +} +static const char *configure_checkhandler(cmd_parms *cmd, void *d_, int arg) +{ + dir_config_rec *d = d_; + + d->checkhandler = arg ? MODDIR_ON : MODDIR_OFF; + return NULL; +} +static const char *configure_redirect(cmd_parms *cmd, void *d_, const char *arg1) +{ + dir_config_rec *d = d_; + int status; + + if (!strcasecmp(arg1, "ON")) + status = HTTP_MOVED_TEMPORARILY; + else if (!strcasecmp(arg1, "OFF")) + status = REDIRECT_OFF; + else if (!strcasecmp(arg1, "permanent")) + status = HTTP_MOVED_PERMANENTLY; + else if (!strcasecmp(arg1, "temp")) + status = HTTP_MOVED_TEMPORARILY; + else if (!strcasecmp(arg1, "seeother")) + status = HTTP_SEE_OTHER; + else if (apr_isdigit(*arg1)) { + status = atoi(arg1); + if (!ap_is_HTTP_REDIRECT(status)) { + return "DirectoryIndexRedirect only accepts values between 300 and 399"; + } + } + else { + return "DirectoryIndexRedirect ON|OFF|permanent|temp|seeother|3xx"; + } + + d->redirect_index = status; + return NULL; +} +static const command_rec dir_cmds[] = +{ + AP_INIT_TAKE1("FallbackResource", ap_set_string_slot, + (void*)APR_OFFSETOF(dir_config_rec, dflt), + DIR_CMD_PERMS, "Set a default handler"), + AP_INIT_RAW_ARGS("DirectoryIndex", add_index, NULL, DIR_CMD_PERMS, + "a list of file names"), + AP_INIT_FLAG("DirectorySlash", configure_slash, NULL, DIR_CMD_PERMS, + "On or Off"), + AP_INIT_FLAG("DirectoryCheckHandler", configure_checkhandler, NULL, DIR_CMD_PERMS, + "On or Off"), + AP_INIT_TAKE1("DirectoryIndexRedirect", configure_redirect, + NULL, DIR_CMD_PERMS, "On, Off, or a 3xx status code."), + + {NULL} +}; + +static void *create_dir_config(apr_pool_t *p, char *dummy) +{ + dir_config_rec *new = apr_pcalloc(p, sizeof(dir_config_rec)); + + new->index_names = NULL; + new->do_slash = MODDIR_UNSET; + new->checkhandler = MODDIR_UNSET; + new->redirect_index = REDIRECT_UNSET; + return (void *) new; +} + +static void *merge_dir_configs(apr_pool_t *p, void *basev, void *addv) +{ + dir_config_rec *new = apr_pcalloc(p, sizeof(dir_config_rec)); + dir_config_rec *base = (dir_config_rec *)basev; + dir_config_rec *add = (dir_config_rec *)addv; + + new->index_names = add->index_names ? add->index_names : base->index_names; + new->do_slash = + (add->do_slash == MODDIR_UNSET) ? base->do_slash : add->do_slash; + new->checkhandler = + (add->checkhandler == MODDIR_UNSET) ? base->checkhandler : add->checkhandler; + new->redirect_index= + (add->redirect_index == REDIRECT_UNSET) ? base->redirect_index : add->redirect_index; + new->dflt = add->dflt ? add->dflt : base->dflt; + return new; +} + +static int fixup_dflt(request_rec *r) +{ + dir_config_rec *d = ap_get_module_config(r->per_dir_config, &dir_module); + const char *name_ptr; + request_rec *rr; + int error_notfound = 0; + + name_ptr = d->dflt; + if ((name_ptr == NULL) || !(strcasecmp(name_ptr,"disabled"))){ + return DECLINED; + } + /* XXX: if FallbackResource points to something that doesn't exist, + * this may recurse until it hits the limit for internal redirects + * before returning an Internal Server Error. + */ + + /* The logic of this function is basically cloned and simplified + * from fixup_dir below. See the comments there. + */ + if (r->args != NULL) { + name_ptr = apr_pstrcat(r->pool, name_ptr, "?", r->args, NULL); + } + rr = ap_sub_req_lookup_uri(name_ptr, r, r->output_filters); + if (rr->status == HTTP_OK + && ( (rr->handler && !strcmp(rr->handler, "proxy-server")) + || rr->finfo.filetype == APR_REG)) { + ap_internal_fast_redirect(rr, r); + return OK; + } + else if (ap_is_HTTP_REDIRECT(rr->status)) { + + apr_pool_join(r->pool, rr->pool); + r->notes = apr_table_overlay(r->pool, r->notes, rr->notes); + r->headers_out = apr_table_overlay(r->pool, r->headers_out, + rr->headers_out); + r->err_headers_out = apr_table_overlay(r->pool, r->err_headers_out, + rr->err_headers_out); + error_notfound = rr->status; + } + else if (rr->status && rr->status != HTTP_NOT_FOUND + && rr->status != HTTP_OK) { + error_notfound = rr->status; + } + + ap_destroy_sub_req(rr); + if (error_notfound) { + return error_notfound; + } + + /* nothing for us to do, pass on through */ + return DECLINED; +} + +static int fixup_dir(request_rec *r) +{ + dir_config_rec *d; + char *dummy_ptr[1]; + char **names_ptr; + int num_names; + int error_notfound = 0; + + /* In case mod_mime wasn't present, and no handler was assigned. */ + if (!r->handler) { + r->handler = DIR_MAGIC_TYPE; + } + + /* Never tolerate path_info on dir requests */ + if (r->path_info && *r->path_info) { + return DECLINED; + } + + d = (dir_config_rec *)ap_get_module_config(r->per_dir_config, + &dir_module); + + /* Redirect requests that are not '/' terminated */ + if (r->uri[0] == '\0' || r->uri[strlen(r->uri) - 1] != '/') + { + char *ifile; + + if (!d->do_slash) { + return DECLINED; + } + + /* Only redirect non-get requests if we have no note to warn + * that this browser cannot handle redirs on non-GET requests + * (such as Microsoft's WebFolders). + */ + if ((r->method_number != M_GET) + && apr_table_get(r->subprocess_env, "redirect-carefully")) { + return DECLINED; + } + + if (r->args != NULL) { + ifile = apr_pstrcat(r->pool, ap_escape_uri(r->pool, r->uri), + "/?", r->args, NULL); + } + else { + ifile = apr_pstrcat(r->pool, ap_escape_uri(r->pool, r->uri), + "/", NULL); + } + + apr_table_setn(r->headers_out, "Location", + ap_construct_url(r->pool, ifile, r)); + return HTTP_MOVED_PERMANENTLY; + } + + /* we're running between mod_rewrites fixup and its internal redirect handler, step aside */ + if (!strcmp(r->handler, REWRITE_REDIRECT_HANDLER_NAME)) { + /* Prevent DIR_MAGIC_TYPE from leaking out when someone has taken over */ + if (!strcmp(r->content_type, DIR_MAGIC_TYPE)) { + r->content_type = NULL; + } + return DECLINED; + } + + if (d->checkhandler == MODDIR_ON && strcmp(r->handler, DIR_MAGIC_TYPE)) { + /* Prevent DIR_MAGIC_TYPE from leaking out when someone has taken over */ + if (!strcmp(r->content_type, DIR_MAGIC_TYPE)) { + r->content_type = NULL; + } + return DECLINED; + } + + if (d->index_names) { + names_ptr = (char **)d->index_names->elts; + num_names = d->index_names->nelts; + } + else { + dummy_ptr[0] = AP_DEFAULT_INDEX; + names_ptr = dummy_ptr; + num_names = 1; + } + + for (; num_names; ++names_ptr, --num_names) { + /* XXX: Is this name_ptr considered escaped yet, or not??? */ + char *name_ptr = *names_ptr; + request_rec *rr; + + /* Once upon a time args were handled _after_ the successful redirect. + * But that redirect might then _refuse_ the given r->args, creating + * a nasty tangle. It seems safer to consider the r->args while we + * determine if name_ptr is our viable index, and therefore set them + * up correctly on redirect. + */ + if (r->args != NULL) { + name_ptr = apr_pstrcat(r->pool, name_ptr, "?", r->args, NULL); + } + + rr = ap_sub_req_lookup_uri(name_ptr, r, r->output_filters); + + /* The sub request lookup is very liberal, and the core map_to_storage + * handler will almost always result in HTTP_OK as /foo/index.html + * may be /foo with PATH_INFO="/index.html", or even / with + * PATH_INFO="/foo/index.html". To get around this we insist that the + * the index be a regular filetype. + * + * Another reason is that the core handler also makes the assumption + * that if r->finfo is still NULL by the time it gets called, the + * file does not exist. + */ + if (rr->status == HTTP_OK + && ( (rr->handler && !strcmp(rr->handler, "proxy-server")) + || rr->finfo.filetype == APR_REG)) { + + if (ap_is_HTTP_REDIRECT(d->redirect_index)) { + apr_table_setn(r->headers_out, "Location", ap_construct_url(r->pool, rr->uri, r)); + return d->redirect_index; + } + + ap_internal_fast_redirect(rr, r); + return OK; + } + + /* If the request returned a redirect, propagate it to the client */ + + if (ap_is_HTTP_REDIRECT(rr->status) + || (rr->status == HTTP_NOT_ACCEPTABLE && num_names == 1) + || (rr->status == HTTP_UNAUTHORIZED && num_names == 1)) { + + apr_pool_join(r->pool, rr->pool); + error_notfound = rr->status; + r->notes = apr_table_overlay(r->pool, r->notes, rr->notes); + r->headers_out = apr_table_overlay(r->pool, r->headers_out, + rr->headers_out); + r->err_headers_out = apr_table_overlay(r->pool, r->err_headers_out, + rr->err_headers_out); + return error_notfound; + } + + /* If the request returned something other than 404 (or 200), + * it means the module encountered some sort of problem. To be + * secure, we should return the error, rather than allow autoindex + * to create a (possibly unsafe) directory index. + * + * So we store the error, and if none of the listed files + * exist, we return the last error response we got, instead + * of a directory listing. + */ + if (rr->status && rr->status != HTTP_NOT_FOUND + && rr->status != HTTP_OK) { + error_notfound = rr->status; + } + + ap_destroy_sub_req(rr); + } + + if (error_notfound) { + return error_notfound; + } + + /* record what we tried, mostly for the benefit of mod_autoindex */ + apr_table_setn(r->notes, "dir-index-names", + d->index_names ? + apr_array_pstrcat(r->pool, d->index_names, ',') : + AP_DEFAULT_INDEX); + + /* nothing for us to do, pass on through */ + return DECLINED; +} + +static int dir_fixups(request_rec *r) +{ + if (r->finfo.filetype == APR_DIR) { + /* serve up a directory */ + return fixup_dir(r); + } + else if ((r->finfo.filetype == APR_NOFILE) && (r->handler == NULL)) { + /* No handler and nothing in the filesystem - use fallback */ + return fixup_dflt(r); + } + return DECLINED; +} + +static void register_hooks(apr_pool_t *p) +{ + ap_hook_fixups(dir_fixups,NULL,NULL,APR_HOOK_LAST); +} + +AP_DECLARE_MODULE(dir) = { + STANDARD20_MODULE_STUFF, + create_dir_config, /* create per-directory config structure */ + merge_dir_configs, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + dir_cmds, /* command apr_table_t */ + register_hooks /* register hooks */ +}; diff --git a/modules/mappers/mod_dir.dep b/modules/mappers/mod_dir.dep new file mode 100644 index 0000000..e862f70 --- /dev/null +++ b/modules/mappers/mod_dir.dep @@ -0,0 +1,60 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_dir.mak + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + + +.\mod_dir.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_script.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + ".\mod_rewrite.h"\ + diff --git a/modules/mappers/mod_dir.dsp b/modules/mappers/mod_dir.dsp new file mode 100644 index 0000000..cc9ebdb --- /dev/null +++ b/modules/mappers/mod_dir.dsp @@ -0,0 +1,111 @@ +# Microsoft Developer Studio Project File - Name="mod_dir" - 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_dir - 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_dir.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_dir.mak" CFG="mod_dir - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_dir - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_dir - 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_dir - 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_dir_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_dir.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_dir.so" /d LONG_NAME="dir_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_dir.so" /base:@..\..\os\win32\BaseAddr.ref,mod_dir.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_dir.so" /base:@..\..\os\win32\BaseAddr.ref,mod_dir.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_dir.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_dir - 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_dir_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_dir.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_dir.so" /d LONG_NAME="dir_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_dir.so" /base:@..\..\os\win32\BaseAddr.ref,mod_dir.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_dir.so" /base:@..\..\os\win32\BaseAddr.ref,mod_dir.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_dir.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_dir - Win32 Release" +# Name "mod_dir - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_dir.c +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/mappers/mod_dir.exp b/modules/mappers/mod_dir.exp new file mode 100644 index 0000000..5fbf772 --- /dev/null +++ b/modules/mappers/mod_dir.exp @@ -0,0 +1 @@ +dir_module diff --git a/modules/mappers/mod_dir.mak b/modules/mappers/mod_dir.mak new file mode 100644 index 0000000..d490010 --- /dev/null +++ b/modules/mappers/mod_dir.mak @@ -0,0 +1,353 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_dir.dsp +!IF "$(CFG)" == "" +CFG=mod_dir - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_dir - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_dir - Win32 Release" && "$(CFG)" != "mod_dir - 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_dir.mak" CFG="mod_dir - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_dir - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_dir - 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_dir - 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_dir.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_dir.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_dir.obj" + -@erase "$(INTDIR)\mod_dir.res" + -@erase "$(INTDIR)\mod_dir_src.idb" + -@erase "$(INTDIR)\mod_dir_src.pdb" + -@erase "$(OUTDIR)\mod_dir.exp" + -@erase "$(OUTDIR)\mod_dir.lib" + -@erase "$(OUTDIR)\mod_dir.pdb" + -@erase "$(OUTDIR)\mod_dir.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_dir_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_dir.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_dir.so" /d LONG_NAME="dir_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_dir.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_dir.pdb" /debug /out:"$(OUTDIR)\mod_dir.so" /implib:"$(OUTDIR)\mod_dir.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_dir.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_dir.obj" \ + "$(INTDIR)\mod_dir.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" + +"$(OUTDIR)\mod_dir.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_dir.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_dir.so" + if exist .\Release\mod_dir.so.manifest mt.exe -manifest .\Release\mod_dir.so.manifest -outputresource:.\Release\mod_dir.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_dir - 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_dir.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_dir.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_dir.obj" + -@erase "$(INTDIR)\mod_dir.res" + -@erase "$(INTDIR)\mod_dir_src.idb" + -@erase "$(INTDIR)\mod_dir_src.pdb" + -@erase "$(OUTDIR)\mod_dir.exp" + -@erase "$(OUTDIR)\mod_dir.lib" + -@erase "$(OUTDIR)\mod_dir.pdb" + -@erase "$(OUTDIR)\mod_dir.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_dir_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_dir.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_dir.so" /d LONG_NAME="dir_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_dir.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_dir.pdb" /debug /out:"$(OUTDIR)\mod_dir.so" /implib:"$(OUTDIR)\mod_dir.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_dir.so +LINK32_OBJS= \ + "$(INTDIR)\mod_dir.obj" \ + "$(INTDIR)\mod_dir.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" + +"$(OUTDIR)\mod_dir.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_dir.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_dir.so" + if exist .\Debug\mod_dir.so.manifest mt.exe -manifest .\Debug\mod_dir.so.manifest -outputresource:.\Debug\mod_dir.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_dir.dep") +!INCLUDE "mod_dir.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_dir.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_dir - Win32 Release" || "$(CFG)" == "mod_dir - Win32 Debug" + +!IF "$(CFG)" == "mod_dir - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\mappers" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_dir - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\mappers" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ENDIF + +!IF "$(CFG)" == "mod_dir - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\mappers" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_dir - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\mappers" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ENDIF + +!IF "$(CFG)" == "mod_dir - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\mappers" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_dir - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\mappers" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\mappers" + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_dir - Win32 Release" + + +"$(INTDIR)\mod_dir.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_dir.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_dir.so" /d LONG_NAME="dir_module for Apache" $(SOURCE) + + +!ELSEIF "$(CFG)" == "mod_dir - Win32 Debug" + + +"$(INTDIR)\mod_dir.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_dir.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_dir.so" /d LONG_NAME="dir_module for Apache" $(SOURCE) + + +!ENDIF + +SOURCE=.\mod_dir.c + +"$(INTDIR)\mod_dir.obj" : $(SOURCE) "$(INTDIR)" + + + +!ENDIF + diff --git a/modules/mappers/mod_imagemap.c b/modules/mappers/mod_imagemap.c new file mode 100644 index 0000000..206c0b6 --- /dev/null +++ b/modules/mappers/mod_imagemap.c @@ -0,0 +1,897 @@ +/* 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. + */ + +/* + * This imagemap module started as a port of the original imagemap.c + * written by Rob McCool (11/13/93 robm@ncsa.uiuc.edu). + * This version includes the mapping algorithms found in version 1.3 + * of imagemap.c. + * + * Contributors to this code include: + * + * Kevin Hughes, kevinh@pulua.hcc.hawaii.edu + * + * Eric Haines, erich@eye.com + * "macmartinized" polygon code copyright 1992 by Eric Haines, erich@eye.com + * + * Randy Terbush, randy@zyzzyva.com + * port to Apache module format, "base_uri" and support for relative URLs + * + * James H. Cloos, Jr., cloos@jhcloos.com + * Added point datatype, using code in NCSA's version 1.8 imagemap.c + * program, as distributed with version 1.4.1 of their server. + * The point code is originally added by Craig Milo Rogers, Rogers@ISI.Edu + * + * Nathan Kurz, nate@tripod.com + * Rewrite/reorganization. New handling of default, base and relative URLs. + * New Configuration directives: + * ImapMenu {none, formatted, semiformatted, unformatted} + * ImapDefault {error, nocontent, referer, menu, URL} + * ImapBase {map, referer, URL} + * Support for creating non-graphical menu added. (backwards compatible): + * Old: directive URL [x,y ...] + * New: directive URL "Menu text" [x,y ...] + * or: directive URL x,y ... "Menu text" + * Map format and menu concept courtesy Joshua Bell, jsbell@acs.ucalgary.ca. + * + * Mark Cox, mark@ukweb.com, Allow relative URLs even when no base specified + */ + +#include "apr.h" +#include "apr_strings.h" +#include "apr_lib.h" + +#define APR_WANT_STDIO /* for sscanf() */ +#define APR_WANT_STRFUNC +#include "apr_want.h" + +#include "ap_config.h" +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_main.h" +#include "http_log.h" +#include "util_script.h" +#include "mod_core.h" + + +#define IMAP_MAGIC_TYPE "application/x-httpd-imap" +#define MAXVERTS 100 +#define X 0 +#define Y 1 + +#define IMAP_MENU_DEFAULT "formatted" +#define IMAP_DEFAULT_DEFAULT "nocontent" +#define IMAP_BASE_DEFAULT "map" + +module AP_MODULE_DECLARE_DATA imagemap_module; + +typedef struct { + char *imap_menu; + char *imap_default; + char *imap_base; +} imap_conf_rec; + +static void *create_imap_dir_config(apr_pool_t *p, char *dummy) +{ + imap_conf_rec *icr = + (imap_conf_rec *) apr_palloc(p, sizeof(imap_conf_rec)); + + icr->imap_menu = NULL; + icr->imap_default = NULL; + icr->imap_base = NULL; + + return icr; +} + +static void *merge_imap_dir_configs(apr_pool_t *p, void *basev, void *addv) +{ + imap_conf_rec *new = (imap_conf_rec *) apr_palloc(p, sizeof(imap_conf_rec)); + imap_conf_rec *base = (imap_conf_rec *) basev; + imap_conf_rec *add = (imap_conf_rec *) addv; + + new->imap_menu = add->imap_menu ? add->imap_menu : base->imap_menu; + new->imap_default = add->imap_default ? add->imap_default + : base->imap_default; + new->imap_base = add->imap_base ? add->imap_base : base->imap_base; + + return new; +} + + +static const command_rec imap_cmds[] = +{ + AP_INIT_TAKE1("ImapMenu", ap_set_string_slot, + (void *)APR_OFFSETOF(imap_conf_rec, imap_menu), OR_INDEXES, + "the type of menu generated: none, formatted, semiformatted, " + "unformatted"), + AP_INIT_TAKE1("ImapDefault", ap_set_string_slot, + (void *)APR_OFFSETOF(imap_conf_rec, imap_default), OR_INDEXES, + "the action taken if no match: error, nocontent, referer, " + "menu, URL"), + AP_INIT_TAKE1("ImapBase", ap_set_string_slot, + (void *)APR_OFFSETOF(imap_conf_rec, imap_base), OR_INDEXES, + "the base for all URL's: map, referer, URL (or start of)"), + {NULL} +}; + +static int pointinrect(const double point[2], double coords[MAXVERTS][2]) +{ + double max[2], min[2]; + if (coords[0][X] > coords[1][X]) { + max[0] = coords[0][X]; + min[0] = coords[1][X]; + } + else { + max[0] = coords[1][X]; + min[0] = coords[0][X]; + } + + if (coords[0][Y] > coords[1][Y]) { + max[1] = coords[0][Y]; + min[1] = coords[1][Y]; + } + else { + max[1] = coords[1][Y]; + min[1] = coords[0][Y]; + } + + return ((point[X] >= min[0] && point[X] <= max[0]) && + (point[Y] >= min[1] && point[Y] <= max[1])); +} + +static int pointincircle(const double point[2], double coords[MAXVERTS][2]) +{ + double radius1, radius2; + + radius1 = ((coords[0][Y] - coords[1][Y]) * (coords[0][Y] - coords[1][Y])) + + ((coords[0][X] - coords[1][X]) * (coords[0][X] - coords[1][X])); + + radius2 = ((coords[0][Y] - point[Y]) * (coords[0][Y] - point[Y])) + + ((coords[0][X] - point[X]) * (coords[0][X] - point[X])); + + return (radius2 <= radius1); +} + +#define fmin(a,b) (((a)>(b))?(b):(a)) +#define fmax(a,b) (((a)>(b))?(a):(b)) + +static int pointinpoly(const double point[2], double pgon[MAXVERTS][2]) +{ + int i, numverts, crossings = 0; + double x = point[X], y = point[Y]; + + for (numverts = 0; numverts < MAXVERTS && pgon[numverts][X] != -1; + numverts++) { + /* just counting the vertexes */ + } + + for (i = 0; i < numverts; i++) { + double x1=pgon[i][X]; + double y1=pgon[i][Y]; + double x2=pgon[(i + 1) % numverts][X]; + double y2=pgon[(i + 1) % numverts][Y]; + double d=(y - y1) * (x2 - x1) - (x - x1) * (y2 - y1); + + if ((y1 >= y) != (y2 >= y)) { + crossings +=y2 - y1 >= 0 ? d >= 0 : d <= 0; + } + if (!d && fmin(x1,x2) <= x && x <= fmax(x1,x2) + && fmin(y1,y2) <= y && y <= fmax(y1,y2)) { + return 1; + } + } + return crossings & 0x01; +} + + +static int is_closer(const double point[2], double coords[MAXVERTS][2], + double *closest) +{ + double dist_squared = ((point[X] - coords[0][X]) + * (point[X] - coords[0][X])) + + ((point[Y] - coords[0][Y]) + * (point[Y] - coords[0][Y])); + + if (point[X] < 0 || point[Y] < 0) { + return (0); /* don't mess around with negative coordinates */ + } + + if (*closest < 0 || dist_squared < *closest) { + *closest = dist_squared; + return (1); /* if this is the first point or is the closest yet + set 'closest' equal to this distance^2 */ + } + + return (0); /* if it's not the first or closest */ + +} + +static double get_x_coord(const char *args) +{ + char *endptr; /* we want it non-null */ + double x_coord = -1; /* -1 is returned if no coordinate is given */ + + if (args == NULL) { + return (-1); /* in case we aren't passed anything */ + } + + while (*args && !apr_isdigit(*args) && *args != ',') { + args++; /* jump to the first digit, but not past + a comma or end */ + } + + x_coord = strtod(args, &endptr); + + if (endptr > args) { /* if a conversion was made */ + return (x_coord); + } + + return (-1); /* else if no conversion was made, + or if no args was given */ +} + +static double get_y_coord(const char *args) +{ + char *endptr; /* we want it non-null */ + const char *start_of_y = NULL; + double y_coord = -1; /* -1 is returned on error */ + + if (args == NULL) { + return (-1); /* in case we aren't passed anything */ + } + + start_of_y = ap_strchr_c(args, ','); /* the comma */ + + if (start_of_y) { + + start_of_y++; /* start looking at the character after + the comma */ + + while (*start_of_y && !apr_isdigit(*start_of_y)) { + start_of_y++; /* jump to the first digit, but not + past the end */ + } + + y_coord = strtod(start_of_y, &endptr); + + if (endptr > start_of_y) { + return (y_coord); + } + } + + return (-1); /* if no conversion was made, or + no comma was found in args */ +} + + +/* See if string has a "quoted part", and if so set *quoted_part to + * the first character of the quoted part, then hammer a \0 onto the + * trailing quote, and set *string to point at the first character + * past the second quote. + * + * Otherwise set *quoted_part to NULL, and leave *string alone. + */ +static void read_quoted(char **string, char **quoted_part) +{ + char *strp = *string; + + /* assume there's no quoted part */ + *quoted_part = NULL; + + while (apr_isspace(*strp)) { + strp++; /* go along string until non-whitespace */ + } + + if (*strp == '"') { /* if that character is a double quote */ + strp++; /* step over it */ + *quoted_part = strp; /* note where the quoted part begins */ + + while (*strp && *strp != '"') { + ++strp; /* skip the quoted portion */ + } + + *strp = '\0'; /* end the string with a NUL */ + + strp++; /* step over the last double quote */ + *string = strp; + } +} + +/* + * returns the mapped URL or NULL. + */ +static const char *imap_url(request_rec *r, const char *base, const char *value) +{ +/* translates a value into a URL. */ + apr_size_t slen, clen; + char *string_pos = NULL; + const char *string_pos_const = NULL; + char *directory = NULL; + const char *referer = NULL; + char *my_base; + + if (!strcasecmp(value, "map") || !strcasecmp(value, "menu")) { + return ap_construct_url(r->pool, r->uri, r); + } + + if (!strcasecmp(value, "nocontent") || !strcasecmp(value, "error")) { + return apr_pstrdup(r->pool, value); /* these are handled elsewhere, + so just copy them */ + } + + if (!strcasecmp(value, "referer")) { + referer = apr_table_get(r->headers_in, "Referer"); + if (referer && *referer) { + return referer; + } + else { + /* XXX: This used to do *value = '\0'; ... which is totally bogus + * because it hammers the passed in value, which can be a string + * constant, or part of a config, or whatever. Total garbage. + * This works around that without changing the rest of this + * code much + */ + value = ""; /* if 'referer' but no referring page, + null the value */ + } + } + + string_pos_const = value; + while (apr_isalpha(*string_pos_const)) { + string_pos_const++; /* go along the URL from the map + until a non-letter */ + } + if (*string_pos_const == ':') { + /* if letters and then a colon (like http:) */ + /* it's an absolute URL, so use it! */ + return apr_pstrdup(r->pool, value); + } + + if (!base || !*base) { + if (value && *value) { + return apr_pstrdup(r->pool, value); /* no base: use what is given */ + } + /* no base, no value: pick a simple default */ + return ap_construct_url(r->pool, "/", r); + } + + /* must be a relative URL to be combined with base */ + if (ap_strchr_c(base, '/') == NULL && (!strncmp(value, "../", 3) + || !strcmp(value, ".."))) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00677) + "invalid base directive in map file: %s", r->uri); + return NULL; + } + my_base = apr_pstrdup(r->pool, base); + string_pos = my_base; + while (*string_pos) { + if (*string_pos == '/' && *(string_pos + 1) == '/') { + string_pos += 2; /* if there are two slashes, jump over them */ + continue; + } + if (*string_pos == '/') { /* the first single slash */ + if (value[0] == '/') { + *string_pos = '\0'; + } /* if the URL from the map starts from root, + end the base URL string at the first single + slash */ + else { + directory = string_pos; /* save the start of + the directory portion */ + + string_pos = strrchr(string_pos, '/'); /* now reuse + string_pos */ + string_pos++; /* step over that last slash */ + *string_pos = '\0'; + } /* but if the map url is relative, leave the + slash on the base (if there is one) */ + break; + } + string_pos++; /* until we get to the end of my_base without + finding a slash by itself */ + } + + while (!strncmp(value, "../", 3) || !strcmp(value, "..")) { + + if (directory && (slen = strlen(directory))) { + + /* for each '..', knock a directory off the end + by ending the string right at the last slash. + But only consider the directory portion: don't eat + into the server name. And only try if a directory + portion was found */ + + clen = slen - 1; + + while ((slen - clen) == 1) { + + if ((string_pos = strrchr(directory, '/'))) { + *string_pos = '\0'; + } + clen = strlen(directory); + if (clen == 0) { + break; + } + } + + value += 2; /* jump over the '..' that we found in the + value */ + } + else if (directory) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00678) + "invalid directory name in map file: %s", r->uri); + return NULL; + } + + if (!strncmp(value, "/../", 4) || !strcmp(value, "/..")) { + value++; /* step over the '/' if there are more '..' + to do. This way, we leave the starting + '/' on value after the last '..', but get + rid of it otherwise */ + } + + } /* by this point, value does not start + with '..' */ + + if (value && *value) { + return apr_pstrcat(r->pool, my_base, value, NULL); + } + return my_base; +} + +static int imap_reply(request_rec *r, const char *redirect) +{ + if (!strcasecmp(redirect, "error")) { + /* they actually requested an error! */ + return HTTP_INTERNAL_SERVER_ERROR; + } + if (!strcasecmp(redirect, "nocontent")) { + /* tell the client to keep the page it has */ + return HTTP_NO_CONTENT; + } + if (redirect && *redirect) { + /* must be a URL, so redirect to it */ + apr_table_setn(r->headers_out, "Location", redirect); + return HTTP_MOVED_TEMPORARILY; + } + return HTTP_INTERNAL_SERVER_ERROR; +} + +static void menu_header(request_rec *r, char *menu) +{ + ap_set_content_type(r, "text/html; charset=ISO-8859-1"); + + ap_rvputs(r, DOCTYPE_HTML_3_2, "<html><head>\n<title>Menu for ", + ap_escape_html(r->pool, r->uri), + "</title>\n</head><body>\n", NULL); + + if (!strcasecmp(menu, "formatted")) { + ap_rvputs(r, "<h1>Menu for ", + ap_escape_html(r->pool, r->uri), + "</h1>\n<hr />\n\n", NULL); + } +} + +static void menu_blank(request_rec *r, char *menu) +{ + if (!strcasecmp(menu, "formatted")) { + ap_rputs("\n", r); + } + else if (!strcasecmp(menu, "semiformatted")) { + ap_rputs("<br />\n", r); + } + else if (!strcasecmp(menu, "unformatted")) { + ap_rputs("\n", r); + } +} + +static void menu_comment(request_rec *r, char *menu, char *comment) +{ + /* comments are ignored in the 'formatted' form */ + if (!strcasecmp(menu, "formatted")) { + ap_rputs("\n", r); /* print just a newline if 'formatted' */ + } + else if (!strcasecmp(menu, "semiformatted") && *comment) { + ap_rvputs(r, comment, "\n", NULL); + } + else if (!strcasecmp(menu, "unformatted") && *comment) { + ap_rvputs(r, comment, "\n", NULL); + } +} + +static void menu_default(request_rec *r, const char *menu, const char *href, const char *text) +{ + char *ehref, *etext; + if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) { + return; /* don't print such lines, these aren't + really href's */ + } + + ehref = ap_escape_uri(r->pool, href); + etext = ap_escape_html(r->pool, text); + + if (!strcasecmp(menu, "formatted")) { + ap_rvputs(r, "<pre>(Default) <a href=\"", ehref, "\">", etext, + "</a></pre>\n", NULL); + } + else if (!strcasecmp(menu, "semiformatted")) { + ap_rvputs(r, "<pre>(Default) <a href=\"", ehref, "\">", etext, + "</a></pre>\n", NULL); + } + else if (!strcasecmp(menu, "unformatted")) { + ap_rvputs(r, "<a href=\"", ehref, "\">", etext, "</a>", NULL); + } +} + +static void menu_directive(request_rec *r, const char *menu, const char *href, const char *text) +{ + char *ehref, *etext; + if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) { + return; /* don't print such lines, as this isn't + really an href */ + } + + ehref = ap_escape_uri(r->pool, href); + etext = ap_escape_html(r->pool, text); + + if (!strcasecmp(menu, "formatted")) { + ap_rvputs(r, "<pre> <a href=\"", ehref, "\">", etext, + "</a></pre>\n", NULL); + } + else if (!strcasecmp(menu, "semiformatted")) { + ap_rvputs(r, "<pre> <a href=\"", ehref, "\">", etext, + "</a></pre>\n", NULL); + } + else if (!strcasecmp(menu, "unformatted")) { + ap_rvputs(r, "<a href=\"", ehref, "\">", etext, "</a>", NULL); + } +} + +static void menu_footer(request_rec *r) +{ + ap_rputs("\n\n</body>\n</html>\n", r); /* finish the menu */ +} + +static int imap_handler_internal(request_rec *r) +{ + char input[MAX_STRING_LEN]; + char *directive; + char *value; + char *href_text; + const char *base; + const char *redirect; + const char *mapdflt; + char *closest = NULL; + double closest_yet = -1; + apr_status_t status; + + double testpoint[2]; + double pointarray[MAXVERTS + 1][2]; + int vertex; + + char *string_pos; + int showmenu = 0; + + imap_conf_rec *icr; + + char *imap_menu; + char *imap_default; + char *imap_base; + + ap_configfile_t *imap; + + icr = ap_get_module_config(r->per_dir_config, &imagemap_module); + + imap_menu = icr->imap_menu ? icr->imap_menu : IMAP_MENU_DEFAULT; + imap_default = icr->imap_default + ? icr->imap_default : IMAP_DEFAULT_DEFAULT; + imap_base = icr->imap_base ? icr->imap_base : IMAP_BASE_DEFAULT; + + status = ap_pcfg_openfile(&imap, r->pool, r->filename); + + if (status != APR_SUCCESS) { + return HTTP_NOT_FOUND; + } + + base = imap_url(r, NULL, imap_base); /* set base according + to default */ + if (!base) { + return HTTP_INTERNAL_SERVER_ERROR; + } + mapdflt = imap_url(r, NULL, imap_default); /* and default to + global default */ + if (!mapdflt) { + return HTTP_INTERNAL_SERVER_ERROR; + } + + testpoint[X] = get_x_coord(r->args); + testpoint[Y] = get_y_coord(r->args); + + if ((testpoint[X] == -1 || testpoint[Y] == -1) || + (testpoint[X] == 0 && testpoint[Y] == 0)) { + /* if either is -1 or if both are zero (new Lynx) */ + /* we don't have valid coordinates */ + testpoint[X] = -1; + testpoint[Y] = -1; + if (strncasecmp(imap_menu, "none", 2)) { + showmenu = 1; /* show the menu _unless_ ImapMenu is + 'none' or 'no' */ + } + } + + if (showmenu) { /* send start of imagemap menu if + we're going to */ + menu_header(r, imap_menu); + } + + while (!ap_cfg_getline(input, sizeof(input), imap)) { + if (!input[0]) { + if (showmenu) { + menu_blank(r, imap_menu); + } + continue; + } + + if (input[0] == '#') { + if (showmenu) { + menu_comment(r, imap_menu, input + 1); + } + continue; + } /* blank lines and comments are ignored + if we aren't printing a menu */ + + /* find the first two space delimited fields, recall that + * ap_cfg_getline has removed leading/trailing whitespace. + * + * note that we're tokenizing as we go... if we were to use the + * ap_getword() class of functions we would end up allocating extra + * memory for every line of the map file + */ + string_pos = input; + if (!*string_pos) { /* need at least two fields */ + goto need_2_fields; + } + + directive = string_pos; + while (*string_pos && !apr_isspace(*string_pos)) { /* past directive */ + ++string_pos; + } + if (!*string_pos) { /* need at least two fields */ + goto need_2_fields; + } + *string_pos++ = '\0'; + + if (!*string_pos) { /* need at least two fields */ + goto need_2_fields; + } + while (apr_isspace(*string_pos)) { /* past whitespace */ + ++string_pos; + } + + value = string_pos; + while (*string_pos && !apr_isspace(*string_pos)) { /* past value */ + ++string_pos; + } + if (apr_isspace(*string_pos)) { + *string_pos++ = '\0'; + } + else { + /* end of input, don't advance past it */ + *string_pos = '\0'; + } + + if (!strncasecmp(directive, "base", 4)) { /* base, base_uri */ + base = imap_url(r, NULL, value); + if (!base) { + goto menu_bail; + } + continue; /* base is never printed to a menu */ + } + + read_quoted(&string_pos, &href_text); + + if (!strcasecmp(directive, "default")) { /* default */ + mapdflt = imap_url(r, NULL, value); + if (!mapdflt) { + goto menu_bail; + } + if (showmenu) { /* print the default if there's a menu */ + redirect = imap_url(r, base, mapdflt); + if (!redirect) { + goto menu_bail; + } + menu_default(r, imap_menu, redirect, + href_text ? href_text : mapdflt); + } + continue; + } + + vertex = 0; + while (vertex < MAXVERTS && + sscanf(string_pos, "%lf%*[, ]%lf", + &pointarray[vertex][X], &pointarray[vertex][Y]) == 2) { + /* Now skip what we just read... we can't use ANSIism %n */ + while (apr_isspace(*string_pos)) { /* past whitespace */ + string_pos++; + } + while (apr_isdigit(*string_pos)) { /* and the 1st number */ + string_pos++; + } + string_pos++; /* skip the ',' */ + while (apr_isspace(*string_pos)) { /* past any more whitespace */ + string_pos++; + } + while (apr_isdigit(*string_pos)) { /* 2nd number */ + string_pos++; + } + vertex++; + } /* so long as there are more vertices to + read, and we have room, read them in. + We start where we left off of the last + sscanf, not at the beginning. */ + + pointarray[vertex][X] = -1; /* signals the end of vertices */ + + if (showmenu) { + if (!href_text) { + read_quoted(&string_pos, &href_text); /* href text could + be here instead */ + } + redirect = imap_url(r, base, value); + if (!redirect) { + goto menu_bail; + } + menu_directive(r, imap_menu, redirect, + href_text ? href_text : value); + continue; + } + /* note that we don't make it past here if we are making a menu */ + + if (testpoint[X] == -1 || pointarray[0][X] == -1) { + continue; /* don't try the following tests if testpoints + are invalid, or if there are no + coordinates */ + } + + if (!strcasecmp(directive, "poly")) { /* poly */ + + if (pointinpoly(testpoint, pointarray)) { + ap_cfg_closefile(imap); + redirect = imap_url(r, base, value); + if (!redirect) { + return HTTP_INTERNAL_SERVER_ERROR; + } + return (imap_reply(r, redirect)); + } + continue; + } + + if (!strcasecmp(directive, "circle")) { /* circle */ + + if (pointincircle(testpoint, pointarray)) { + ap_cfg_closefile(imap); + redirect = imap_url(r, base, value); + if (!redirect) { + return HTTP_INTERNAL_SERVER_ERROR; + } + return (imap_reply(r, redirect)); + } + continue; + } + + if (!strcasecmp(directive, "rect")) { /* rect */ + + if (pointinrect(testpoint, pointarray)) { + ap_cfg_closefile(imap); + redirect = imap_url(r, base, value); + if (!redirect) { + return HTTP_INTERNAL_SERVER_ERROR; + } + return (imap_reply(r, redirect)); + } + continue; + } + + if (!strcasecmp(directive, "point")) { /* point */ + + if (is_closer(testpoint, pointarray, &closest_yet)) { + closest = apr_pstrdup(r->pool, value); + } + + continue; + } /* move on to next line whether it's + closest or not */ + + } /* nothing matched, so we get another line! */ + + ap_cfg_closefile(imap); /* we are done with the map file; close it */ + + if (showmenu) { + menu_footer(r); /* finish the menu and we are done */ + return OK; + } + + if (closest) { /* if a 'point' directive has been seen */ + redirect = imap_url(r, base, closest); + if (!redirect) { + return HTTP_INTERNAL_SERVER_ERROR; + } + return (imap_reply(r, redirect)); + } + + if (mapdflt) { /* a default should be defined, even if + only 'nocontent' */ + redirect = imap_url(r, base, mapdflt); + if (!redirect) { + return HTTP_INTERNAL_SERVER_ERROR; + } + return (imap_reply(r, redirect)); + } + + return HTTP_INTERNAL_SERVER_ERROR; /* If we make it this far, + we failed. They lose! */ + +need_2_fields: + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00679) + "map file %s, line %d syntax error: requires at " + "least two fields", r->uri, imap->line_number); + /* fall through */ +menu_bail: + ap_cfg_closefile(imap); + if (showmenu) { + /* There's not much else we can do ... we've already sent the headers + * to the client. + */ + ap_rputs("\n\n[an internal server error occured]\n", r); + menu_footer(r); + return OK; + } + return HTTP_INTERNAL_SERVER_ERROR; +} + +static int imap_handler(request_rec *r) +{ + /* Optimization: skip the allocation of large local variables on the + * stack (in imap_handler_internal()) on requests that aren't using + * imagemaps + */ + if (r->method_number != M_GET || (strcmp(r->handler,IMAP_MAGIC_TYPE) + && strcmp(r->handler, "imap-file"))) { + return DECLINED; + } + else { + return imap_handler_internal(r); + } +} + +static void register_hooks(apr_pool_t *p) +{ + ap_hook_handler(imap_handler,NULL,NULL,APR_HOOK_MIDDLE); +} + +AP_DECLARE_MODULE(imagemap) = +{ + STANDARD20_MODULE_STUFF, + create_imap_dir_config, /* dir config creater */ + merge_imap_dir_configs, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + imap_cmds, /* command apr_table_t */ + register_hooks /* register hooks */ +}; diff --git a/modules/mappers/mod_imagemap.dep b/modules/mappers/mod_imagemap.dep new file mode 100644 index 0000000..357895e --- /dev/null +++ b/modules/mappers/mod_imagemap.dep @@ -0,0 +1,60 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_imagemap.mak + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + + +.\mod_imagemap.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\httpd.h"\ + "..\..\include\mod_core.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_script.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + diff --git a/modules/mappers/mod_imagemap.dsp b/modules/mappers/mod_imagemap.dsp new file mode 100644 index 0000000..88a85c8 --- /dev/null +++ b/modules/mappers/mod_imagemap.dsp @@ -0,0 +1,111 @@ +# Microsoft Developer Studio Project File - Name="mod_imagemap" - 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_imagemap - 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_imagemap.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_imagemap.mak" CFG="mod_imagemap - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_imagemap - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_imagemap - 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_imagemap - 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_imagemap_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_imagemap.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_imagemap.so" /d LONG_NAME="imagemap_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_imagemap.so" /base:@..\..\os\win32\BaseAddr.ref,mod_imagemap.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_imagemap.so" /base:@..\..\os\win32\BaseAddr.ref,mod_imagemap.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_imagemap.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_imagemap - 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_imagemap_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_imagemap.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_imagemap.so" /d LONG_NAME="imagemap_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_imagemap.so" /base:@..\..\os\win32\BaseAddr.ref,mod_imagemap.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_imagemap.so" /base:@..\..\os\win32\BaseAddr.ref,mod_imagemap.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_imagemap.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_imagemap - Win32 Release" +# Name "mod_imagemap - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_imagemap.c +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/mappers/mod_imagemap.exp b/modules/mappers/mod_imagemap.exp new file mode 100644 index 0000000..1e0e0b8 --- /dev/null +++ b/modules/mappers/mod_imagemap.exp @@ -0,0 +1 @@ +imap_module diff --git a/modules/mappers/mod_imagemap.mak b/modules/mappers/mod_imagemap.mak new file mode 100644 index 0000000..da50a61 --- /dev/null +++ b/modules/mappers/mod_imagemap.mak @@ -0,0 +1,353 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_imagemap.dsp +!IF "$(CFG)" == "" +CFG=mod_imagemap - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_imagemap - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_imagemap - Win32 Release" && "$(CFG)" != "mod_imagemap - 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_imagemap.mak" CFG="mod_imagemap - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_imagemap - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_imagemap - 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_imagemap - 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_imagemap.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_imagemap.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_imagemap.obj" + -@erase "$(INTDIR)\mod_imagemap.res" + -@erase "$(INTDIR)\mod_imagemap_src.idb" + -@erase "$(INTDIR)\mod_imagemap_src.pdb" + -@erase "$(OUTDIR)\mod_imagemap.exp" + -@erase "$(OUTDIR)\mod_imagemap.lib" + -@erase "$(OUTDIR)\mod_imagemap.pdb" + -@erase "$(OUTDIR)\mod_imagemap.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_imagemap_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_imagemap.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_imagemap.so" /d LONG_NAME="imagemap_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_imagemap.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_imagemap.pdb" /debug /out:"$(OUTDIR)\mod_imagemap.so" /implib:"$(OUTDIR)\mod_imagemap.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_imagemap.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_imagemap.obj" \ + "$(INTDIR)\mod_imagemap.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" + +"$(OUTDIR)\mod_imagemap.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_imagemap.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_imagemap.so" + if exist .\Release\mod_imagemap.so.manifest mt.exe -manifest .\Release\mod_imagemap.so.manifest -outputresource:.\Release\mod_imagemap.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_imagemap - 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_imagemap.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_imagemap.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_imagemap.obj" + -@erase "$(INTDIR)\mod_imagemap.res" + -@erase "$(INTDIR)\mod_imagemap_src.idb" + -@erase "$(INTDIR)\mod_imagemap_src.pdb" + -@erase "$(OUTDIR)\mod_imagemap.exp" + -@erase "$(OUTDIR)\mod_imagemap.lib" + -@erase "$(OUTDIR)\mod_imagemap.pdb" + -@erase "$(OUTDIR)\mod_imagemap.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_imagemap_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_imagemap.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_imagemap.so" /d LONG_NAME="imagemap_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_imagemap.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_imagemap.pdb" /debug /out:"$(OUTDIR)\mod_imagemap.so" /implib:"$(OUTDIR)\mod_imagemap.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_imagemap.so +LINK32_OBJS= \ + "$(INTDIR)\mod_imagemap.obj" \ + "$(INTDIR)\mod_imagemap.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" + +"$(OUTDIR)\mod_imagemap.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_imagemap.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_imagemap.so" + if exist .\Debug\mod_imagemap.so.manifest mt.exe -manifest .\Debug\mod_imagemap.so.manifest -outputresource:.\Debug\mod_imagemap.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_imagemap.dep") +!INCLUDE "mod_imagemap.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_imagemap.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_imagemap - Win32 Release" || "$(CFG)" == "mod_imagemap - Win32 Debug" + +!IF "$(CFG)" == "mod_imagemap - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\mappers" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_imagemap - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\mappers" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ENDIF + +!IF "$(CFG)" == "mod_imagemap - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\mappers" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_imagemap - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\mappers" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ENDIF + +!IF "$(CFG)" == "mod_imagemap - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\mappers" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_imagemap - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\mappers" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\mappers" + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_imagemap - Win32 Release" + + +"$(INTDIR)\mod_imagemap.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_imagemap.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_imagemap.so" /d LONG_NAME="imagemap_module for Apache" $(SOURCE) + + +!ELSEIF "$(CFG)" == "mod_imagemap - Win32 Debug" + + +"$(INTDIR)\mod_imagemap.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_imagemap.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_imagemap.so" /d LONG_NAME="imagemap_module for Apache" $(SOURCE) + + +!ENDIF + +SOURCE=.\mod_imagemap.c + +"$(INTDIR)\mod_imagemap.obj" : $(SOURCE) "$(INTDIR)" + + + +!ENDIF + diff --git a/modules/mappers/mod_negotiation.c b/modules/mappers/mod_negotiation.c new file mode 100644 index 0000000..c056b28 --- /dev/null +++ b/modules/mappers/mod_negotiation.c @@ -0,0 +1,3223 @@ +/* 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_negotiation.c: keeps track of MIME types the client is willing to + * accept, and contains code to handle type arbitration. + * + * rst + */ + +#include "apr.h" +#include "apr_strings.h" +#include "apr_file_io.h" +#include "apr_lib.h" + +#define APR_WANT_STRFUNC +#include "apr_want.h" + +#include "ap_config.h" +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_protocol.h" +#include "http_core.h" +#include "http_log.h" +#include "util_script.h" + + +#define MAP_FILE_MAGIC_TYPE "application/x-type-map" + +/* Commands --- configuring document caching on a per (virtual?) + * server basis... + */ + +typedef struct { + int forcelangpriority; + apr_array_header_t *language_priority; +} neg_dir_config; + +/* forcelangpriority flags + */ +#define FLP_UNDEF 0 /* Same as FLP_DEFAULT, but base overrides */ +#define FLP_NONE 1 /* Return 406, HTTP_NOT_ACCEPTABLE */ +#define FLP_PREFER 2 /* Use language_priority rather than MC */ +#define FLP_FALLBACK 4 /* Use language_priority rather than NA */ + +#define FLP_DEFAULT FLP_PREFER + +/* env evaluation + */ +#define DISCARD_ALL_ENCODINGS 1 /* no-gzip */ +#define DISCARD_ALL_BUT_HTML 2 /* gzip-only-text/html */ + +module AP_MODULE_DECLARE_DATA negotiation_module; + +static void *create_neg_dir_config(apr_pool_t *p, char *dummy) +{ + neg_dir_config *new = (neg_dir_config *) apr_palloc(p, + sizeof(neg_dir_config)); + + new->forcelangpriority = FLP_UNDEF; + new->language_priority = NULL; + return new; +} + +static void *merge_neg_dir_configs(apr_pool_t *p, void *basev, void *addv) +{ + neg_dir_config *base = (neg_dir_config *) basev; + neg_dir_config *add = (neg_dir_config *) addv; + neg_dir_config *new = (neg_dir_config *) apr_palloc(p, + sizeof(neg_dir_config)); + + /* give priority to the config in the subdirectory */ + new->forcelangpriority = (add->forcelangpriority != FLP_UNDEF) + ? add->forcelangpriority + : base->forcelangpriority; + new->language_priority = add->language_priority + ? add->language_priority + : base->language_priority; + return new; +} + +static const char *set_language_priority(cmd_parms *cmd, void *n_, + const char *lang) +{ + neg_dir_config *n = n_; + const char **langp; + + if (!n->language_priority) + n->language_priority = apr_array_make(cmd->pool, 4, sizeof(char *)); + + langp = (const char **) apr_array_push(n->language_priority); + *langp = lang; + return NULL; +} + +static const char *set_force_priority(cmd_parms *cmd, void *n_, const char *w) +{ + neg_dir_config *n = n_; + + if (!strcasecmp(w, "None")) { + if (n->forcelangpriority & ~FLP_NONE) { + return "Cannot combine ForceLanguagePriority options with None"; + } + n->forcelangpriority = FLP_NONE; + } + else if (!strcasecmp(w, "Prefer")) { + if (n->forcelangpriority & FLP_NONE) { + return "Cannot combine ForceLanguagePriority options None and " + "Prefer"; + } + n->forcelangpriority |= FLP_PREFER; + } + else if (!strcasecmp(w, "Fallback")) { + if (n->forcelangpriority & FLP_NONE) { + return "Cannot combine ForceLanguagePriority options None and " + "Fallback"; + } + n->forcelangpriority |= FLP_FALLBACK; + } + else { + return apr_pstrcat(cmd->pool, "Invalid ForceLanguagePriority option ", + w, NULL); + } + + return NULL; +} + +static const char *cache_negotiated_docs(cmd_parms *cmd, void *dummy, + int arg) +{ + ap_set_module_config(cmd->server->module_config, &negotiation_module, + (arg ? "Cache" : NULL)); + return NULL; +} + +static int do_cache_negotiated_docs(server_rec *s) +{ + return (ap_get_module_config(s->module_config, + &negotiation_module) != NULL); +} + +static const command_rec negotiation_cmds[] = +{ + AP_INIT_FLAG("CacheNegotiatedDocs", cache_negotiated_docs, NULL, RSRC_CONF, + "Either 'on' or 'off' (default)"), + AP_INIT_ITERATE("LanguagePriority", set_language_priority, NULL, + OR_FILEINFO, + "space-delimited list of MIME language abbreviations"), + AP_INIT_ITERATE("ForceLanguagePriority", set_force_priority, NULL, + OR_FILEINFO, + "Force LanguagePriority elections, either None, or " + "Fallback and/or Prefer"), + {NULL} +}; + +/* + * Record of available info on a media type specified by the client + * (we also use 'em for encodings and languages) + */ + +typedef struct accept_rec { + char *name; /* MUST be lowercase */ + float quality; + float level; + char *charset; /* for content-type only */ +} accept_rec; + +/* + * Record of available info on a particular variant + * + * Note that a few of these fields are updated by the actual negotiation + * code. These are: + * + * level_matched --- initialized to zero. Set to the value of level + * if the client actually accepts this media type at that + * level (and *not* if it got in on a wildcard). See level_cmp + * below. + * mime_stars -- initialized to zero. Set to the number of stars + * present in the best matching Accept header element. + * 1 for star/star, 2 for type/star and 3 for + * type/subtype. + * + * definite -- initialized to 1. Set to 0 if there is a match which + * makes the variant non-definite according to the rules + * in rfc2296. + */ + +typedef struct var_rec { + request_rec *sub_req; /* May be NULL (is, for map files) */ + const char *mime_type; /* MUST be lowercase */ + const char *file_name; /* Set to 'this' (for map file body content) */ + apr_off_t body; /* Only for map file body content */ + const char *content_encoding; + apr_array_header_t *content_languages; /* list of lang. for this variant */ + const char *content_charset; + const char *description; + + /* The next five items give the quality values for the dimensions + * of negotiation for this variant. They are obtained from the + * appropriate header lines, except for source_quality, which + * is obtained from the variant itself (the 'qs' parameter value + * from the variant's mime-type). Apart from source_quality, + * these values are set when we find the quality for each variant + * (see best_match()). source_quality is set from the 'qs' parameter + * of the variant description or mime type: see set_mime_fields(). + */ + float lang_quality; /* quality of this variant's language */ + float encoding_quality; /* ditto encoding */ + float charset_quality; /* ditto charset */ + float mime_type_quality; /* ditto media type */ + float source_quality; /* source quality for this variant */ + + /* Now some special values */ + float level; /* Auxiliary to content-type... */ + apr_off_t bytes; /* content length, if known */ + int lang_index; /* Index into LanguagePriority list */ + int is_pseudo_html; /* text/html, *or* the INCLUDES_MAGIC_TYPEs */ + + /* Above are all written-once properties of the variant. The + * three fields below are changed during negotiation: + */ + + float level_matched; + int mime_stars; + int definite; +} var_rec; + +/* Something to carry around the state of negotiation (and to keep + * all of this thread-safe)... + */ + +typedef struct { + apr_pool_t *pool; + request_rec *r; + neg_dir_config *conf; + char *dir_name; + int accept_q; /* 1 if an Accept item has a q= param */ + float default_lang_quality; /* fiddle lang q for variants with no lang */ + + /* the array pointers below are NULL if the corresponding accept + * headers are not present + */ + apr_array_header_t *accepts; /* accept_recs */ + apr_array_header_t *accept_encodings; /* accept_recs */ + apr_array_header_t *accept_charsets; /* accept_recs */ + apr_array_header_t *accept_langs; /* accept_recs */ + + apr_array_header_t *avail_vars; /* available variants */ + + int count_multiviews_variants; /* number of variants found on disk */ + + int is_transparent; /* 1 if this resource is trans. negotiable */ + + int dont_fiddle_headers; /* 1 if we may not fiddle with accept hdrs */ + int ua_supports_trans; /* 1 if ua supports trans negotiation */ + int send_alternates; /* 1 if we want to send an Alternates header */ + int may_choose; /* 1 if we may choose a variant for the client */ + int use_rvsa; /* 1 if we must use RVSA/1.0 negotiation algo */ +} negotiation_state; + +/* A few functions to manipulate var_recs. + * Cleaning out the fields... + */ + +static void clean_var_rec(var_rec *mime_info) +{ + mime_info->sub_req = NULL; + mime_info->mime_type = ""; + mime_info->file_name = ""; + mime_info->body = 0; + mime_info->content_encoding = NULL; + mime_info->content_languages = NULL; + mime_info->content_charset = ""; + mime_info->description = ""; + + mime_info->is_pseudo_html = 0; + mime_info->level = 0.0f; + mime_info->level_matched = 0.0f; + mime_info->bytes = -1; + mime_info->lang_index = -1; + mime_info->mime_stars = 0; + mime_info->definite = 1; + + mime_info->charset_quality = 1.0f; + mime_info->encoding_quality = 1.0f; + mime_info->lang_quality = 1.0f; + mime_info->mime_type_quality = 1.0f; + mime_info->source_quality = 0.0f; +} + +/* Initializing the relevant fields of a variant record from the + * accept_info read out of its content-type, one way or another. + */ + +static void set_mime_fields(var_rec *var, accept_rec *mime_info) +{ + var->mime_type = mime_info->name; + var->source_quality = mime_info->quality; + var->level = mime_info->level; + var->content_charset = mime_info->charset; + + var->is_pseudo_html = (!strcmp(var->mime_type, "text/html") + || !strcmp(var->mime_type, INCLUDES_MAGIC_TYPE) + || !strcmp(var->mime_type, INCLUDES_MAGIC_TYPE3)); +} + +/* Create a variant list validator in r using info from vlistr. */ + +static void set_vlist_validator(request_rec *r, request_rec *vlistr) +{ + /* Calculating the variant list validator is similar to + * calculating an etag for the source of the variant list + * information, so we use ap_make_etag(). Note that this + * validator can be 'weak' in extreme case. + */ + ap_update_mtime(vlistr, vlistr->finfo.mtime); + r->vlist_validator = ap_make_etag(vlistr, 0); + + /* ap_set_etag will later take r->vlist_validator into account + * when creating the etag header + */ +} + + +/***************************************************************** + * + * Parsing (lists of) media types and their parameters, as seen in + * HTTPD header lines and elsewhere. + */ + +/* + * parse quality value. atof(3) is not well-usable here, because it + * depends on the locale (argh). + * + * However, RFC 2616 states: + * 3.9 Quality Values + * + * [...] HTTP/1.1 applications MUST NOT generate more than three digits + * after the decimal point. User configuration of these values SHOULD also + * be limited in this fashion. + * + * qvalue = ( "0" [ "." 0*3DIGIT ] ) + * | ( "1" [ "." 0*3("0") ] ) + * + * This is quite easy. If the supplied string doesn't match the above + * definition (loosely), we simply return 1 (same as if there's no qvalue) + */ + +static float atoq(const char *string) +{ + if (!string || !*string) { + return 1.0f; + } + + while (apr_isspace(*string)) { + ++string; + } + + /* be tolerant and accept qvalues without leading zero + * (also for backwards compat, where atof() was in use) + */ + if (*string != '.' && *string++ != '0') { + return 1.0f; + } + + if (*string == '.') { + /* better only one division later, than dealing with fscking + * IEEE format 0.1 factors ... + */ + int i = 0; + + if (*++string >= '0' && *string <= '9') { + i += (*string - '0') * 100; + + if (*++string >= '0' && *string <= '9') { + i += (*string - '0') * 10; + + if (*++string > '0' && *string <= '9') { + i += (*string - '0'); + } + } + } + + return (float)i / 1000.0f; + } + + return 0.0f; +} + +/* + * Get a single mime type entry --- one media type and parameters; + * enter the values we recognize into the argument accept_rec + */ + +static const char *get_entry(apr_pool_t *p, accept_rec *result, + const char *accept_line) +{ + result->quality = 1.0f; + result->level = 0.0f; + result->charset = ""; + + /* + * Note that this handles what I gather is the "old format", + * + * Accept: text/html text/plain moo/zot + * + * without any compatibility kludges --- if the token after the + * MIME type begins with a semicolon, we know we're looking at parms, + * otherwise, we know we aren't. (So why all the pissing and moaning + * in the CERN server code? I must be missing something). + */ + + result->name = ap_get_token(p, &accept_line, 0); + ap_str_tolower(result->name); /* You want case insensitive, + * you'll *get* case insensitive. + */ + + /* KLUDGE!!! Default HTML to level 2.0 unless the browser + * *explicitly* says something else. + */ + + if (!strcmp(result->name, "text/html") && (result->level == 0.0)) { + result->level = 2.0f; + } + else if (!strcmp(result->name, INCLUDES_MAGIC_TYPE)) { + result->level = 2.0f; + } + else if (!strcmp(result->name, INCLUDES_MAGIC_TYPE3)) { + result->level = 3.0f; + } + + while (*accept_line == ';') { + /* Parameters ... */ + + char *parm; + char *cp; + char *end; + + ++accept_line; + parm = ap_get_token(p, &accept_line, 1); + + /* Look for 'var = value' --- and make sure the var is in lcase. */ + + for (cp = parm; (*cp && !apr_isspace(*cp) && *cp != '='); ++cp) { + *cp = apr_tolower(*cp); + } + + if (!*cp) { + continue; /* No '='; just ignore it. */ + } + + *cp++ = '\0'; /* Delimit var */ + while (apr_isspace(*cp) || *cp == '=') { + ++cp; + } + + if (*cp == '"') { + ++cp; + for (end = cp; + (*end && *end != '\n' && *end != '\r' && *end != '\"'); + end++); + } + else { + for (end = cp; (*end && !apr_isspace(*end)); end++); + } + if (*end) { + *end = '\0'; /* strip ending quote or return */ + } + ap_str_tolower(cp); + + if (parm[0] == 'q' + && (parm[1] == '\0' || (parm[1] == 's' && parm[2] == '\0'))) { + result->quality = atoq(cp); + } + else if (parm[0] == 'l' && !strcmp(&parm[1], "evel")) { + result->level = (float)atoi(cp); + } + else if (!strcmp(parm, "charset")) { + result->charset = cp; + } + } + + if (*accept_line == ',') { + ++accept_line; + } + + return accept_line; +} + +/***************************************************************** + * + * Dealing with header lines ... + * + * Accept, Accept-Charset, Accept-Language and Accept-Encoding + * are handled by do_header_line() - they all have the same + * basic structure of a list of items of the format + * name; q=N; charset=TEXT + * + * where charset is only valid in Accept. + */ + +static apr_array_header_t *do_header_line(apr_pool_t *p, + const char *accept_line) +{ + apr_array_header_t *accept_recs; + + if (!accept_line) { + return NULL; + } + + accept_recs = apr_array_make(p, 40, sizeof(accept_rec)); + + while (*accept_line) { + accept_rec *new = (accept_rec *) apr_array_push(accept_recs); + accept_line = get_entry(p, new, accept_line); + } + + return accept_recs; +} + +/* Given the text of the Content-Languages: line from the var map file, + * return an array containing the languages of this variant + */ + +static apr_array_header_t *do_languages_line(apr_pool_t *p, + const char **lang_line) +{ + apr_array_header_t *lang_recs = apr_array_make(p, 2, sizeof(char *)); + + if (!lang_line) { + return lang_recs; + } + + while (**lang_line) { + char **new = (char **) apr_array_push(lang_recs); + *new = ap_get_token(p, lang_line, 0); + ap_str_tolower(*new); + if (**lang_line == ',' || **lang_line == ';') { + ++(*lang_line); + } + } + + return lang_recs; +} + +/***************************************************************** + * + * Handling header lines from clients... + */ + +static negotiation_state *parse_accept_headers(request_rec *r) +{ + negotiation_state *new = + (negotiation_state *) apr_pcalloc(r->pool, sizeof(negotiation_state)); + accept_rec *elts; + apr_table_t *hdrs = r->headers_in; + int i; + + new->pool = r->pool; + new->r = r; + new->conf = (neg_dir_config *)ap_get_module_config(r->per_dir_config, + &negotiation_module); + + new->dir_name = ap_make_dirstr_parent(r->pool, r->filename); + + new->accepts = do_header_line(r->pool, apr_table_get(hdrs, "Accept")); + + /* calculate new->accept_q value */ + if (new->accepts) { + elts = (accept_rec *) new->accepts->elts; + + for (i = 0; i < new->accepts->nelts; ++i) { + if (elts[i].quality < 1.0) { + new->accept_q = 1; + } + } + } + + new->accept_encodings = + do_header_line(r->pool, apr_table_get(hdrs, "Accept-Encoding")); + new->accept_langs = + do_header_line(r->pool, apr_table_get(hdrs, "Accept-Language")); + new->accept_charsets = + do_header_line(r->pool, apr_table_get(hdrs, "Accept-Charset")); + + /* This is possibly overkill for some servers, heck, we have + * only 33 index.html variants in docs/docroot (today). + * Make this configurable? + */ + new->avail_vars = apr_array_make(r->pool, 40, sizeof(var_rec)); + + return new; +} + + +static void parse_negotiate_header(request_rec *r, negotiation_state *neg) +{ + const char *negotiate = apr_table_get(r->headers_in, "Negotiate"); + char *tok; + + /* First, default to no TCN, no Alternates, and the original Apache + * negotiation algorithm with fiddles for broken browser configs. + * + * To save network bandwidth, we do not configure to send an + * Alternates header to the user agent by default. User + * agents that want an Alternates header for agent-driven + * negotiation will have to request it by sending an + * appropriate Negotiate header. + */ + neg->ua_supports_trans = 0; + neg->send_alternates = 0; + neg->may_choose = 1; + neg->use_rvsa = 0; + neg->dont_fiddle_headers = 0; + + if (!negotiate) + return; + + if (strcmp(negotiate, "trans") == 0) { + /* Lynx 2.7 and 2.8 send 'negotiate: trans' even though they + * do not support transparent content negotiation, so for Lynx we + * ignore the negotiate header when its contents are exactly "trans". + * If future versions of Lynx ever need to say 'negotiate: trans', + * they can send the equivalent 'negotiate: trans, trans' instead + * to avoid triggering the workaround below. + */ + const char *ua = apr_table_get(r->headers_in, "User-Agent"); + + if (ua && (strncmp(ua, "Lynx", 4) == 0)) + return; + } + + neg->may_choose = 0; /* An empty Negotiate would require 300 response */ + + while ((tok = ap_get_list_item(neg->pool, &negotiate)) != NULL) { + + if (strcmp(tok, "trans") == 0 || + strcmp(tok, "vlist") == 0 || + strcmp(tok, "guess-small") == 0 || + apr_isdigit(tok[0]) || + strcmp(tok, "*") == 0) { + + /* The user agent supports transparent negotiation */ + neg->ua_supports_trans = 1; + + /* Send-alternates could be configurable, but note + * that it must be 1 if we have 'vlist' in the + * negotiate header. + */ + neg->send_alternates = 1; + + if (strcmp(tok, "1.0") == 0) { + /* we may use the RVSA/1.0 algorithm, configure for it */ + neg->may_choose = 1; + neg->use_rvsa = 1; + neg->dont_fiddle_headers = 1; + } + else if (tok[0] == '*') { + /* we may use any variant selection algorithm, configure + * to use the Apache algorithm + */ + neg->may_choose = 1; + + /* We disable header fiddles on the assumption that a + * client sending Negotiate knows how to send correct + * headers which don't need fiddling. + */ + neg->dont_fiddle_headers = 1; + } + } + } + +#ifdef NEG_DEBUG + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00680) + "dont_fiddle_headers=%d use_rvsa=%d ua_supports_trans=%d " + "send_alternates=%d, may_choose=%d", + neg->dont_fiddle_headers, neg->use_rvsa, + neg->ua_supports_trans, neg->send_alternates, neg->may_choose); +#endif + +} + +/* Sometimes clients will give us no Accept info at all; this routine sets + * up the standard default for that case, and also arranges for us to be + * willing to run a CGI script if we find one. (In fact, we set up to + * dramatically prefer CGI scripts in cases where that's appropriate, + * e.g., POST or when URI includes query args or extra path info). + */ +static void maybe_add_default_accepts(negotiation_state *neg, + int prefer_scripts) +{ + accept_rec *new_accept; + + if (!neg->accepts) { + neg->accepts = apr_array_make(neg->pool, 4, sizeof(accept_rec)); + + new_accept = (accept_rec *) apr_array_push(neg->accepts); + + new_accept->name = "*/*"; + new_accept->quality = 1.0f; + new_accept->level = 0.0f; + } + + new_accept = (accept_rec *) apr_array_push(neg->accepts); + + new_accept->name = CGI_MAGIC_TYPE; + if (neg->use_rvsa) { + new_accept->quality = 0; + } + else { + new_accept->quality = prefer_scripts ? 2.0f : 0.001f; + } + new_accept->level = 0.0f; +} + +/***************************************************************** + * + * Parsing type-map files, in Roy's meta/http format augmented with + * #-comments. + */ + +/* Reading RFC822-style header lines, ignoring #-comments and + * handling continuations. + */ + +enum header_state { + header_eof, header_seen, header_sep +}; + +static enum header_state get_header_line(char *buffer, int len, apr_file_t *map) +{ + char *buf_end = buffer + len; + char *cp; + char c; + + /* Get a noncommented line */ + + do { + if (apr_file_gets(buffer, MAX_STRING_LEN, map) != APR_SUCCESS) { + return header_eof; + } + } while (buffer[0] == '#'); + + /* If blank, just return it --- this ends information on this variant */ + + for (cp = buffer; apr_isspace(*cp); ++cp) { + continue; + } + + if (*cp == '\0') { + return header_sep; + } + + /* If non-blank, go looking for header lines, but note that we still + * have to treat comments specially... + */ + + cp += strlen(cp); + + /* We need to shortcut the rest of this block following the Body: + * tag - we will not look for continutation after this line. + */ + if (!ap_cstr_casecmpn(buffer, "Body:", 5)) + return header_seen; + + while (apr_file_getc(&c, map) != APR_EOF) { + if (c == '#') { + /* Comment line */ + while (apr_file_getc(&c, map) != APR_EOF && c != '\n') { + continue; + } + } + else if (apr_isspace(c)) { + /* Leading whitespace. POSSIBLE continuation line + * Also, possibly blank --- if so, we ungetc() the final newline + * so that we will pick up the blank line the next time 'round. + */ + + while (c != '\n' && apr_isspace(c)) { + if (apr_file_getc(&c, map) != APR_SUCCESS) { + break; + } + } + + apr_file_ungetc(c, map); + + if (c == '\n') { + return header_seen; /* Blank line */ + } + + /* Continuation */ + + while ( cp < buf_end - 2 + && (apr_file_getc(&c, map)) != APR_EOF + && c != '\n') { + *cp++ = c; + } + + *cp++ = '\n'; + *cp = '\0'; + } + else { + + /* Line beginning with something other than whitespace */ + + apr_file_ungetc(c, map); + return header_seen; + } + } + + return header_seen; +} + +static apr_off_t get_body(char *buffer, apr_size_t *len, const char *tag, + apr_file_t *map) +{ + char *endbody; + apr_size_t bodylen; + apr_off_t pos; + + + /* We are at the first character following a body:tag\n entry + * Suck in the body, then backspace to the first char after the + * closing tag entry. If we fail to read, find the tag or back + * up then we have a hosed file, so give up already + */ + --*len; /* Reserve space for '\0' */ + if (apr_file_read(map, buffer, len) != APR_SUCCESS) { + return -1; + } + buffer[*len] = '\0'; + + endbody = ap_strstr(buffer, tag); + if (!endbody) { + return -1; + } + bodylen = endbody - buffer; + endbody += strlen(tag); + /* Skip all the trailing cruft after the end tag to the next line */ + while (*endbody) { + if (*endbody == '\n') { + ++endbody; + break; + } + ++endbody; + } + + pos = -(apr_off_t)(*len - (endbody - buffer)); + if (apr_file_seek(map, APR_CUR, &pos) != APR_SUCCESS) { + return -1; + } + + /* Give the caller back the actual body's file offset and length */ + *len = bodylen; + return pos - (endbody - buffer); +} + + +/* Stripping out RFC822 comments */ + +static void strip_paren_comments(char *hdr) +{ + /* Hmmm... is this correct? In Roy's latest draft, (comments) can nest! */ + /* Nope, it isn't correct. Fails to handle backslash escape as well. */ + + while (*hdr) { + if (*hdr == '"') { + hdr = strchr(hdr, '"'); + if (hdr == NULL) { + return; + } + ++hdr; + } + else if (*hdr == '(') { + while (*hdr && *hdr != ')') { + *hdr++ = ' '; + } + + if (*hdr) { + *hdr++ = ' '; + } + } + else { + ++hdr; + } + } +} + +/* Getting to a header body from the header */ + +static char *lcase_header_name_return_body(char *header, request_rec *r) +{ + char *cp = header; + + for ( ; *cp && *cp != ':' ; ++cp) { + *cp = apr_tolower(*cp); + } + + if (!*cp) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00681) + "Syntax error in type map, no ':' in %s for header %s", + r->filename, header); + return NULL; + } + + do { + ++cp; + } while (apr_isspace(*cp)); + + if (!*cp) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00682) + "Syntax error in type map --- no header body: %s for %s", + r->filename, header); + return NULL; + } + + return cp; +} + +static int read_type_map(apr_file_t **map, negotiation_state *neg, + request_rec *rr) +{ + request_rec *r = neg->r; + apr_file_t *map_ = NULL; + apr_status_t status; + char buffer[MAX_STRING_LEN]; + enum header_state hstate; + struct var_rec mime_info; + int has_content; + + if (!map) + map = &map_; + + /* We are not using multiviews */ + neg->count_multiviews_variants = 0; + + if ((status = apr_file_open(map, rr->filename, APR_READ | APR_BUFFERED, + APR_OS_DEFAULT, neg->pool)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(00683) + "cannot access type map file: %s", rr->filename); + if (APR_STATUS_IS_ENOTDIR(status) || APR_STATUS_IS_ENOENT(status)) { + return HTTP_NOT_FOUND; + } + else { + return HTTP_FORBIDDEN; + } + } + + clean_var_rec(&mime_info); + has_content = 0; + + do { + hstate = get_header_line(buffer, MAX_STRING_LEN, *map); + + if (hstate == header_seen) { + char *body1 = lcase_header_name_return_body(buffer, neg->r); + const char *body; + + if (body1 == NULL) { + return HTTP_INTERNAL_SERVER_ERROR; + } + + strip_paren_comments(body1); + body = body1; + + if (!strncmp(buffer, "uri:", 4)) { + mime_info.file_name = ap_get_token(neg->pool, &body, 0); + } + else if (!strncmp(buffer, "content-type:", 13)) { + struct accept_rec accept_info; + + get_entry(neg->pool, &accept_info, body); + set_mime_fields(&mime_info, &accept_info); + has_content = 1; + } + else if (!strncmp(buffer, "content-length:", 15)) { + apr_off_t clen; + + body1 = ap_get_token(neg->pool, &body, 0); + if (!ap_parse_strict_length(&clen, body1)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00684) + "Parse error in type map, Content-Length: " + "'%s' in %s is invalid.", + body1, r->filename); + break; + } + mime_info.bytes = clen; + has_content = 1; + } + else if (!strncmp(buffer, "content-language:", 17)) { + mime_info.content_languages = do_languages_line(neg->pool, + &body); + has_content = 1; + } + else if (!strncmp(buffer, "content-encoding:", 17)) { + mime_info.content_encoding = ap_get_token(neg->pool, &body, 0); + has_content = 1; + } + else if (!strncmp(buffer, "description:", 12)) { + char *desc = apr_pstrdup(neg->pool, body); + char *cp; + + for (cp = desc; *cp; ++cp) { + if (*cp=='\n') *cp=' '; + } + if (cp>desc) *(cp-1)=0; + mime_info.description = desc; + } + else if (!strncmp(buffer, "body:", 5)) { + char *tag = apr_pstrdup(neg->pool, body); + char *eol = strchr(tag, '\0'); + apr_size_t len = MAX_STRING_LEN; + while (--eol >= tag && apr_isspace(*eol)) + *eol = '\0'; + if ((mime_info.body = get_body(buffer, &len, tag, *map)) < 0) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00685) + "Syntax error in type map, no end tag '%s' " + "found in %s for Body: content.", + tag, r->filename); + break; + } + mime_info.bytes = len; + mime_info.file_name = apr_filepath_name_get(rr->filename); + } + } + else { + if (*mime_info.file_name && has_content) { + void *new_var = apr_array_push(neg->avail_vars); + + memcpy(new_var, (void *) &mime_info, sizeof(var_rec)); + } + + clean_var_rec(&mime_info); + has_content = 0; + } + } while (hstate != header_eof); + + if (map_) + apr_file_close(map_); + + set_vlist_validator(r, rr); + + return OK; +} + +/* Sort function used by read_types_multi. */ +static int variantsortf(var_rec *a, var_rec *b) +{ + /* First key is the source quality, sort in descending order. */ + + /* XXX: note that we currently implement no method of setting the + * source quality for multiviews variants, so we are always comparing + * 1.0 to 1.0 for now + */ + if (a->source_quality < b->source_quality) + return 1; + if (a->source_quality > b->source_quality) + return -1; + + /* Second key is the variant name */ + return strcmp(a->file_name, b->file_name); +} + +/***************************************************************** + * + * Same as read_type_map, except we use a filtered directory listing + * as the map... + */ + +static int read_types_multi(negotiation_state *neg) +{ + request_rec *r = neg->r; + + char *filp; + int prefix_len; + apr_dir_t *dirp; + apr_finfo_t dirent; + apr_status_t status; + struct var_rec mime_info; + struct accept_rec accept_info; + void *new_var; + int anymatch = 0; + + clean_var_rec(&mime_info); + + if (r->proxyreq || !r->filename + || !ap_os_is_path_absolute(neg->pool, r->filename)) { + return DECLINED; + } + + /* Only absolute paths here */ + if (!(filp = strrchr(r->filename, '/'))) { + return DECLINED; + } + ++filp; + prefix_len = strlen(filp); + + if ((status = apr_dir_open(&dirp, neg->dir_name, + neg->pool)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(00686) + "cannot read directory for multi: %s", neg->dir_name); + return HTTP_FORBIDDEN; + } + + while (apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp) == APR_SUCCESS) { + apr_array_header_t *exception_list; + request_rec *sub_req; + + /* Do we have a match? */ +#ifdef CASE_BLIND_FILESYSTEM + if (strncasecmp(dirent.name, filp, prefix_len)) { +#else + if (strncmp(dirent.name, filp, prefix_len)) { +#endif + continue; + } + if (dirent.name[prefix_len] != '.') { + continue; + } + + /* Don't negotiate directories and other unusual files + * Really shouldn't see anything but DIR/LNK/REG here, + * and we aught to discover if the LNK was interesting. + * + * Of course, this only helps platforms that capture the + * the filetype in apr_dir_read(), which most can once + * they are optimized with some magic [it's known to the + * dirent, not associated to the inode, on most FS's.] + */ + if ((dirent.valid & APR_FINFO_TYPE) && (dirent.filetype == APR_DIR)) + continue; + + /* Ok, something's here. Maybe nothing useful. Remember that + * we tried, if we completely fail, so we can reject the request! + */ + anymatch = 1; + + /* See if it's something which we have access to, and which + * has a known type and encoding. + */ + sub_req = ap_sub_req_lookup_dirent(&dirent, r, AP_SUBREQ_MERGE_ARGS, + NULL); + + /* Double check, we still don't multi-resolve non-ordinary files + */ + if (sub_req->finfo.filetype != APR_REG) { + /* XXX sub req not destroyed -- may be a bug/unintentional ? */ + continue; + } + + /* If it has a handler, we'll pretend it's a CGI script, + * since that's a good indication of the sort of thing it + * might be doing. + */ + if (sub_req->handler && !sub_req->content_type) { + ap_set_content_type(sub_req, CGI_MAGIC_TYPE); + } + + /* + * mod_mime will _always_ provide us the base name in the + * ap-mime-exception-list, if it processed anything. If + * this list is empty, give up immediately, there was + * nothing interesting. For example, looking at the files + * readme.txt and readme.foo, we will throw away .foo if + * it's an insignificant file (e.g. did not identify a + * language, charset, encoding, content type or handler,) + */ + exception_list = + (apr_array_header_t *)apr_table_get(sub_req->notes, + "ap-mime-exceptions-list"); + + if (!exception_list) { + ap_destroy_sub_req(sub_req); + continue; + } + + /* Each unregonized bit better match our base name, in sequence. + * A test of index.html.foo will match index.foo or index.html.foo, + * but it will never transpose the segments and allow index.foo.html + * because that would introduce too much CPU consumption. Better that + * we don't attempt a many-to-many match here. + */ + { + int nexcept = exception_list->nelts; + char **cur_except = (char**)exception_list->elts; + char *segstart = filp, *segend, saveend; + + while (*segstart && nexcept) { + if (!(segend = strchr(segstart, '.'))) + segend = strchr(segstart, '\0'); + saveend = *segend; + *segend = '\0'; + +#ifdef CASE_BLIND_FILESYSTEM + if (strcasecmp(segstart, *cur_except) == 0) { +#else + if (strcmp(segstart, *cur_except) == 0) { +#endif + --nexcept; + ++cur_except; + } + + if (!saveend) + break; + + *segend = saveend; + segstart = segend + 1; + } + + if (nexcept) { + /* Something you don't know is, something you don't know... + */ + ap_destroy_sub_req(sub_req); + continue; + } + } + + /* + * If we failed the subrequest, or don't + * know what we are serving, then continue. + */ + if (sub_req->status != HTTP_OK || (!sub_req->content_type)) { + ap_destroy_sub_req(sub_req); + continue; + } + + /* If it's a map file, we use that instead of the map + * we're building... + */ + if (((sub_req->content_type) && + !strcmp(sub_req->content_type, MAP_FILE_MAGIC_TYPE)) || + ((sub_req->handler) && + !strcmp(sub_req->handler, "type-map"))) { + + apr_dir_close(dirp); + neg->avail_vars->nelts = 0; + if (sub_req->status != HTTP_OK) { + return sub_req->status; + } + return read_type_map(NULL, neg, sub_req); + } + + /* Have reasonable variant --- gather notes. */ + + mime_info.sub_req = sub_req; + mime_info.file_name = apr_pstrdup(neg->pool, dirent.name); + if (sub_req->content_encoding) { + mime_info.content_encoding = sub_req->content_encoding; + } + if (sub_req->content_languages) { + mime_info.content_languages = sub_req->content_languages; + } + + get_entry(neg->pool, &accept_info, sub_req->content_type); + set_mime_fields(&mime_info, &accept_info); + + new_var = apr_array_push(neg->avail_vars); + memcpy(new_var, (void *) &mime_info, sizeof(var_rec)); + + neg->count_multiviews_variants++; + + clean_var_rec(&mime_info); + } + + apr_dir_close(dirp); + + /* We found some file names that matched. None could be served. + * Rather than fall out to autoindex or some other mapper, this + * request must die. + */ + if (anymatch && !neg->avail_vars->nelts) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00687) + "Negotiation: discovered file(s) matching request: %s" + " (None could be negotiated).", + r->filename); + return HTTP_NOT_FOUND; + } + + set_vlist_validator(r, r); + + /* Sort the variants into a canonical order. The negotiation + * result sometimes depends on the order of the variants. By + * sorting the variants into a canonical order, rather than using + * the order in which readdir() happens to return them, we ensure + * that the negotiation result will be consistent over filesystem + * backup/restores and over all mirror sites. + */ + + qsort((void *) neg->avail_vars->elts, neg->avail_vars->nelts, + sizeof(var_rec), (int (*)(const void *, const void *)) variantsortf); + + return OK; +} + + +/***************************************************************** + * And now for the code you've been waiting for... actually + * finding a match to the client's requirements. + */ + +/* Matching MIME types ... the star/star and foo/star commenting conventions + * are implemented here. (You know what I mean by star/star, but just + * try mentioning those three characters in a C comment). Using strcmp() + * is legit, because everything has already been smashed to lowercase. + * + * Note also that if we get an exact match on the media type, we update + * level_matched for use in level_cmp below... + * + * We also give a value for mime_stars, which is used later. It should + * be 1 for star/star, 2 for type/star and 3 for type/subtype. + */ + +static int mime_match(accept_rec *accept_r, var_rec *avail) +{ + const char *accept_type = accept_r->name; + const char *avail_type = avail->mime_type; + int len = strlen(accept_type); + + if ((len == 1 && accept_type[0] == '*') + || (len == 3 && !strncmp(accept_type, "*/*", 3))) { + /* Anything matches star or star/star */ + if (avail->mime_stars < 1) { + avail->mime_stars = 1; + } + return 1; + } + else if (len > 2 && accept_type[len - 2] == '/' + && accept_type[len - 1] == '*' + && !strncmp(accept_type, avail_type, len - 2) + && avail_type[len - 2] == '/') { + /* Any subtype matches for type/star */ + if (avail->mime_stars < 2) { + avail->mime_stars = 2; + } + return 1; + } + else if (!strcmp(accept_type, avail_type) + || (!strcmp(accept_type, "text/html") + && (!strcmp(avail_type, INCLUDES_MAGIC_TYPE) + || !strcmp(avail_type, INCLUDES_MAGIC_TYPE3)))) { + if (accept_r->level >= avail->level) { + avail->level_matched = avail->level; + avail->mime_stars = 3; + return 1; + } + } + + return OK; +} + +/* This code implements a piece of the tie-breaking algorithm between + * variants of equal quality. This piece is the treatment of variants + * of the same base media type, but different levels. What we want to + * return is the variant at the highest level that the client explicitly + * claimed to accept. + * + * If all the variants available are at a higher level than that, or if + * the client didn't say anything specific about this media type at all + * and these variants just got in on a wildcard, we prefer the lowest + * level, on grounds that that's the one that the client is least likely + * to choke on. + * + * (This is all motivated by treatment of levels in HTML --- we only + * want to give level 3 to browsers that explicitly ask for it; browsers + * that don't, including HTTP/0.9 browsers that only get the implicit + * "Accept: * / *" [space added to avoid confusing cpp --- no, that + * syntax doesn't really work] should get HTML2 if available). + * + * (Note that this code only comes into play when we are choosing among + * variants of equal quality, where the draft standard gives us a fair + * bit of leeway about what to do. It ain't specified by the standard; + * rather, it is a choice made by this server about what to do in cases + * where the standard does not specify a unique course of action). + */ + +static int level_cmp(var_rec *var1, var_rec *var2) +{ + /* Levels are only comparable between matching media types */ + + if (var1->is_pseudo_html && !var2->is_pseudo_html) { + return 0; + } + + if (!var1->is_pseudo_html && strcmp(var1->mime_type, var2->mime_type)) { + return 0; + } + /* The result of the above if statements is that, if we get to + * here, both variants have the same mime_type or both are + * pseudo-html. + */ + + /* Take highest level that matched, if either did match. */ + + if (var1->level_matched > var2->level_matched) { + return 1; + } + if (var1->level_matched < var2->level_matched) { + return -1; + } + + /* Neither matched. Take lowest level, if there's a difference. */ + + if (var1->level < var2->level) { + return 1; + } + if (var1->level > var2->level) { + return -1; + } + + /* Tied */ + + return 0; +} + +/* Finding languages. The main entry point is set_language_quality() + * which is called for each variant. It sets two elements in the + * variant record: + * language_quality - the 'q' value of the 'best' matching language + * from Accept-Language: header (HTTP/1.1) + * lang_index - Non-negotiated language priority, using + * position of language on the Accept-Language: + * header, if present, else LanguagePriority + * directive order. + * + * When we do the variant checking for best variant, we use language + * quality first, and if a tie, language_index next (this only applies + * when _not_ using the RVSA/1.0 algorithm). If using the RVSA/1.0 + * algorithm, lang_index is never used. + * + * set_language_quality() calls find_lang_index() and find_default_index() + * to set lang_index. + */ + +static int find_lang_index(apr_array_header_t *accept_langs, char *lang) +{ + const char **alang; + int i; + + if (!lang || !accept_langs) { + return -1; + } + + alang = (const char **) accept_langs->elts; + + for (i = 0; i < accept_langs->nelts; ++i) { + if (!ap_cstr_casecmpn(lang, *alang, strlen(*alang))) { + return i; + } + alang += (accept_langs->elt_size / sizeof(char*)); + } + + return -1; +} + +/* set_default_lang_quality() sets the quality we apply to variants + * which have no language assigned to them. If none of the variants + * have a language, we are not negotiating on language, so all are + * acceptable, and we set the default q value to 1.0. However if + * some of the variants have languages, we set this default to 0.0001. + * The value of this default will be applied to all variants with + * no explicit language -- which will have the effect of making them + * acceptable, but only if no variants with an explicit language + * are acceptable. The default q value set here is assigned to variants + * with no language type in set_language_quality(). + * + * Note that if using the RVSA/1.0 algorithm, we don't use this + * fiddle. + */ + +static void set_default_lang_quality(negotiation_state *neg) +{ + var_rec *avail_recs = (var_rec *) neg->avail_vars->elts; + int j; + + if (!neg->dont_fiddle_headers) { + for (j = 0; j < neg->avail_vars->nelts; ++j) { + var_rec *variant = &avail_recs[j]; + if (variant->content_languages && + variant->content_languages->nelts) { + neg->default_lang_quality = 0.0001f; + return; + } + } + } + + neg->default_lang_quality = 1.0f; +} + +/* Set the language_quality value in the variant record. Also + * assigns lang_index for ForceLanguagePriority. + * + * To find the language_quality value, we look for the 'q' value + * of the 'best' matching language on the Accept-Language + * header. The 'best' match is the language on Accept-Language + * header which matches the language of this variant either fully, + * or as far as the prefix marker (-). If two or more languages + * match, use the longest string from the Accept-Language header + * (see HTTP/1.1 [14.4]) + * + * When a variant has multiple languages, we find the 'best' + * match for each variant language tag as above, then select the + * one with the highest q value. Because both the accept-header + * and variant can have multiple languages, we now have a hairy + * loop-within-a-loop here. + * + * If the variant has no language and we have no Accept-Language + * items, leave the quality at 1.0 and return. + * + * If the variant has no language, we use the default as set by + * set_default_lang_quality() (1.0 if we are not negotiating on + * language, 0.001 if we are). + * + * Following the setting of the language quality, we drop through to + * set the old 'lang_index'. This is set based on either the order + * of the languages on the Accept-Language header, or the + * order on the LanguagePriority directive. This is only used + * in the negotiation if the language qualities tie. + */ + +static void set_language_quality(negotiation_state *neg, var_rec *variant) +{ + int forcepriority = neg->conf->forcelangpriority; + if (forcepriority == FLP_UNDEF) { + forcepriority = FLP_DEFAULT; + } + + if (!variant->content_languages || !variant->content_languages->nelts) { + /* This variant has no content-language, so use the default + * quality factor for variants with no content-language + * (previously set by set_default_lang_quality()). + * Leave the factor alone (it remains at 1.0) when we may not fiddle + * with the headers. + */ + if (!neg->dont_fiddle_headers) { + variant->lang_quality = neg->default_lang_quality; + } + return; + } + else { + /* Variant has one (or more) languages. Look for the best + * match. We do this by going through each language on the + * variant description looking for a match on the + * Accept-Language header. The best match is the longest + * matching language on the header. The final result is the + * best q value from all the languages on the variant + * description. + */ + + if (!neg->accept_langs) { + /* no accept-language header makes the variant indefinite */ + variant->definite = 0; + } + else { /* There is an accept-language with 0 or more items */ + accept_rec *accs = (accept_rec *) neg->accept_langs->elts; + accept_rec *best = NULL, *star = NULL; + accept_rec *bestthistag; + char *lang, *p; + float fiddle_q = 0.0f; + int any_match_on_star = 0; + int i, j; + apr_size_t alen, longest_lang_range_len; + + for (j = 0; j < variant->content_languages->nelts; ++j) { + p = NULL; + bestthistag = NULL; + longest_lang_range_len = 0; + + /* lang is the variant's language-tag, which is the one + * we are allowed to use the prefix of in HTTP/1.1 + */ + lang = ((char **) (variant->content_languages->elts))[j]; + + /* now find the best (i.e. longest) matching + * Accept-Language header language. We put the best match + * for this tag in bestthistag. We cannot update the + * overall best (based on q value) because the best match + * for this tag is the longest language item on the accept + * header, not necessarily the highest q. + */ + for (i = 0; i < neg->accept_langs->nelts; ++i) { + if (!strcmp(accs[i].name, "*")) { + if (!star) { + star = &accs[i]; + } + continue; + } + /* Find language. We match if either the variant + * language tag exactly matches the language range + * from the accept header, or a prefix of the variant + * language tag up to a '-' character matches the + * whole of the language range in the Accept-Language + * header. Note that HTTP/1.x allows any number of + * '-' characters in a tag or range, currently only + * tags with zero or one '-' characters are defined + * for general use (see rfc1766). + * + * We only use language range in the Accept-Language + * header the best match for the variant language tag + * if it is longer than the previous best match. + */ + + alen = strlen(accs[i].name); + + if ((strlen(lang) >= alen) && + !strncmp(lang, accs[i].name, alen) && + ((lang[alen] == 0) || (lang[alen] == '-')) ) { + + if (alen > longest_lang_range_len) { + longest_lang_range_len = alen; + bestthistag = &accs[i]; + } + } + + if (!bestthistag && !neg->dont_fiddle_headers) { + /* The next bit is a fiddle. Some browsers might + * be configured to send more specific language + * ranges than desirable. For example, an + * Accept-Language of en-US should never match + * variants with languages en or en-GB. But US + * English speakers might pick en-US as their + * language choice. So this fiddle checks if the + * language range has a prefix, and if so, it + * matches variants which match that prefix with a + * priority of 0.001. So a request for en-US would + * match variants of types en and en-GB, but at + * much lower priority than matches of en-US + * directly, or of any other language listed on + * the Accept-Language header. Note that this + * fiddle does not handle multi-level prefixes. + */ + if ((p = strchr(accs[i].name, '-'))) { + int plen = p - accs[i].name; + + if (!strncmp(lang, accs[i].name, plen)) { + fiddle_q = 0.001f; + } + } + } + } + /* Finished looking at Accept-Language headers, the best + * (longest) match is in bestthistag, or NULL if no match + */ + if (!best || + (bestthistag && bestthistag->quality > best->quality)) { + best = bestthistag; + } + + /* See if the tag matches on a * in the Accept-Language + * header. If so, record this fact for later use + */ + if (!bestthistag && star) { + any_match_on_star = 1; + } + } + + /* If one of the language tags of the variant matched on *, we + * need to see if its q is better than that of any non-* match + * on any other tag of the variant. If so the * match takes + * precedence and the overall match is not definite. + */ + if ( any_match_on_star && + ((best && star->quality > best->quality) || + (!best)) ) { + best = star; + variant->definite = 0; + } + + variant->lang_quality = best ? best->quality : fiddle_q; + } + } + + /* Handle the ForceDefaultLanguage overrides, based on the best match + * to LanguagePriority order. The best match is the lowest index of + * any LanguagePriority match. + */ + if (((forcepriority & FLP_PREFER) + && (variant->lang_index < 0)) + || ((forcepriority & FLP_FALLBACK) + && !variant->lang_quality)) + { + int bestidx = -1; + int j; + + for (j = 0; j < variant->content_languages->nelts; ++j) + { + /* lang is the variant's language-tag, which is the one + * we are allowed to use the prefix of in HTTP/1.1 + */ + char *lang = ((char **) (variant->content_languages->elts))[j]; + int idx; + + /* If we wish to fallback or + * we use our own LanguagePriority index. + */ + idx = find_lang_index(neg->conf->language_priority, lang); + if ((idx >= 0) && ((bestidx == -1) || (idx < bestidx))) { + bestidx = idx; + } + } + + if (bestidx >= 0) { + if (variant->lang_quality) { + if (forcepriority & FLP_PREFER) { + variant->lang_index = bestidx; + } + } + else { + if (forcepriority & FLP_FALLBACK) { + variant->lang_index = bestidx; + variant->lang_quality = .0001f; + variant->definite = 0; + } + } + } + } +} + +/* Determining the content length --- if the map didn't tell us, + * we have to do a stat() and remember for next time. + */ + +static apr_off_t find_content_length(negotiation_state *neg, var_rec *variant) +{ + apr_finfo_t statb; + + if (variant->bytes < 0) { + if ( variant->sub_req + && (variant->sub_req->finfo.valid & APR_FINFO_SIZE)) { + variant->bytes = variant->sub_req->finfo.size; + } + else { + char *fullname = ap_make_full_path(neg->pool, neg->dir_name, + variant->file_name); + + if (apr_stat(&statb, fullname, + APR_FINFO_SIZE, neg->pool) == APR_SUCCESS) { + variant->bytes = statb.size; + } + } + } + + return variant->bytes; +} + +/* For a given variant, find the best matching Accept: header + * and assign the Accept: header's quality value to the + * mime_type_quality field of the variant, for later use in + * determining the best matching variant. + */ + +static void set_accept_quality(negotiation_state *neg, var_rec *variant) +{ + int i; + accept_rec *accept_recs; + float q = 0.0f; + int q_definite = 1; + + /* if no Accept: header, leave quality alone (will + * remain at the default value of 1) + * + * XXX: This if is currently never true because of the effect of + * maybe_add_default_accepts(). + */ + if (!neg->accepts) { + if (variant->mime_type && *variant->mime_type) + variant->definite = 0; + return; + } + + accept_recs = (accept_rec *) neg->accepts->elts; + + /* + * Go through each of the ranges on the Accept: header, + * looking for the 'best' match with this variant's + * content-type. We use the best match's quality + * value (from the Accept: header) for this variant's + * mime_type_quality field. + * + * The best match is determined like this: + * type/type is better than type/ * is better than * / * + * if match is type/type, use the level mime param if available + */ + for (i = 0; i < neg->accepts->nelts; ++i) { + + accept_rec *type = &accept_recs[i]; + int prev_mime_stars; + + prev_mime_stars = variant->mime_stars; + + if (!mime_match(type, variant)) { + continue; /* didn't match the content type at all */ + } + else { + /* did match - see if there were less or more stars than + * in previous match + */ + if (prev_mime_stars == variant->mime_stars) { + continue; /* more stars => not as good a match */ + } + } + + /* If we are allowed to mess with the q-values + * and have no explicit q= parameters in the accept header, + * make wildcards very low, so we have a low chance + * of ending up with them if there's something better. + */ + + if (!neg->dont_fiddle_headers && !neg->accept_q && + variant->mime_stars == 1) { + q = 0.01f; + } + else if (!neg->dont_fiddle_headers && !neg->accept_q && + variant->mime_stars == 2) { + q = 0.02f; + } + else { + q = type->quality; + } + + q_definite = (variant->mime_stars == 3); + } + variant->mime_type_quality = q; + variant->definite = variant->definite && q_definite; + +} + +/* For a given variant, find the 'q' value of the charset given + * on the Accept-Charset line. If no charsets are listed, + * assume value of '1'. + */ +static void set_charset_quality(negotiation_state *neg, var_rec *variant) +{ + int i; + accept_rec *accept_recs; + const char *charset = variant->content_charset; + accept_rec *star = NULL; + + /* if no Accept-Charset: header, leave quality alone (will + * remain at the default value of 1) + */ + if (!neg->accept_charsets) { + if (charset && *charset) + variant->definite = 0; + return; + } + + accept_recs = (accept_rec *) neg->accept_charsets->elts; + + if (charset == NULL || !*charset) { + /* Charset of variant not known */ + + /* if not a text / * type, leave quality alone */ + if (!(!strncmp(variant->mime_type, "text/", 5) + || !strcmp(variant->mime_type, INCLUDES_MAGIC_TYPE) + || !strcmp(variant->mime_type, INCLUDES_MAGIC_TYPE3) + )) + return; + + /* Don't go guessing if we are in strict header mode, + * e.g. when running the rvsa, as any guess won't be reflected + * in the variant list or content-location headers. + */ + if (neg->dont_fiddle_headers) + return; + + charset = "iso-8859-1"; /* The default charset for HTTP text types */ + } + + /* + * Go through each of the items on the Accept-Charset header, + * looking for a match with this variant's charset. If none + * match, charset is unacceptable, so set quality to 0. + */ + for (i = 0; i < neg->accept_charsets->nelts; ++i) { + + accept_rec *type = &accept_recs[i]; + + if (!strcmp(type->name, charset)) { + variant->charset_quality = type->quality; + return; + } + else if (strcmp(type->name, "*") == 0) { + star = type; + } + } + /* No explicit match */ + if (star) { + variant->charset_quality = star->quality; + variant->definite = 0; + return; + } + /* If this variant is in charset iso-8859-1, the default is 1.0 */ + if (strcmp(charset, "iso-8859-1") == 0) { + variant->charset_quality = 1.0f; + } + else { + variant->charset_quality = 0.0f; + } +} + + +/* is_identity_encoding is included for back-compat, but does anyone + * use 7bit, 8bin or binary in their var files?? + */ + +static int is_identity_encoding(const char *enc) +{ + return (!enc || !enc[0] || !strcmp(enc, "7bit") || !strcmp(enc, "8bit") + || !strcmp(enc, "binary")); +} + +/* + * set_encoding_quality determines whether the encoding for a particular + * variant is acceptable for the user-agent. + * + * The rules for encoding are that if the user-agent does not supply + * any Accept-Encoding header, then all encodings are allowed but a + * variant with no encoding should be preferred. + * If there is an empty Accept-Encoding header, then no encodings are + * acceptable. If there is a non-empty Accept-Encoding header, then + * any of the listed encodings are acceptable, as well as no encoding + * unless the "identity" encoding is specifically excluded. + */ +static void set_encoding_quality(negotiation_state *neg, var_rec *variant) +{ + accept_rec *accept_recs; + const char *enc = variant->content_encoding; + accept_rec *star = NULL; + float value_if_not_found = 0.0f; + int i; + + if (!neg->accept_encodings) { + /* We had no Accept-Encoding header, assume that all + * encodings are acceptable with a low quality, + * but we prefer no encoding if available. + */ + if (!enc || is_identity_encoding(enc)) + variant->encoding_quality = 1.0f; + else + variant->encoding_quality = 0.5f; + + return; + } + + if (!enc || is_identity_encoding(enc)) { + enc = "identity"; + value_if_not_found = 0.0001f; + } + + accept_recs = (accept_rec *) neg->accept_encodings->elts; + + /* Go through each of the encodings on the Accept-Encoding: header, + * looking for a match with our encoding. x- prefixes are ignored. + */ + if (enc[0] == 'x' && enc[1] == '-') { + enc += 2; + } + for (i = 0; i < neg->accept_encodings->nelts; ++i) { + + char *name = accept_recs[i].name; + + if (name[0] == 'x' && name[1] == '-') { + name += 2; + } + + if (!strcmp(name, enc)) { + variant->encoding_quality = accept_recs[i].quality; + return; + } + + if (strcmp(name, "*") == 0) { + star = &accept_recs[i]; + } + + } + /* No explicit match */ + if (star) { + variant->encoding_quality = star->quality; + return; + } + + /* Encoding not found on Accept-Encoding: header, so it is + * _not_ acceptable unless it is the identity (no encoding) + */ + variant->encoding_quality = value_if_not_found; +} + +/************************************************************* + * Possible results of the variant selection algorithm + */ +enum algorithm_results { + alg_choice = 1, /* choose variant */ + alg_list /* list variants */ +}; + +/* Below is the 'best_match' function. It returns an int, which has + * one of the two values alg_choice or alg_list, which give the result + * of the variant selection algorithm. alg_list means that no best + * variant was found by the algorithm, alg_choice means that a best + * variant was found and should be returned. The list/choice + * terminology comes from TCN (rfc2295), but is used in a more generic + * way here. The best variant is returned in *pbest. best_match has + * two possible algorithms for determining the best variant: the + * RVSA/1.0 algorithm (from RFC2296), and the standard Apache + * algorithm. These are split out into separate functions + * (is_variant_better_rvsa() and is_variant_better()). Selection of + * one is through the neg->use_rvsa flag. + * + * The call to best_match also creates full information, including + * language, charset, etc quality for _every_ variant. This is needed + * for generating a correct Vary header, and can be used for the + * Alternates header, the human-readable list responses and 406 errors. + */ + +/* Firstly, the RVSA/1.0 (HTTP Remote Variant Selection Algorithm + * v1.0) from rfc2296. This is the algorithm that goes together with + * transparent content negotiation (TCN). + */ +static int is_variant_better_rvsa(negotiation_state *neg, var_rec *variant, + var_rec *best, float *p_bestq) +{ + float bestq = *p_bestq, q; + + /* TCN does not cover negotiation on content-encoding. For now, + * we ignore the encoding unless it was explicitly excluded. + */ + if (variant->encoding_quality == 0.0f) + return 0; + + q = variant->mime_type_quality * + variant->source_quality * + variant->charset_quality * + variant->lang_quality; + + /* RFC 2296 calls for the result to be rounded to 5 decimal places, + * but we don't do that because it serves no useful purpose other + * than to ensure that a remote algorithm operates on the same + * precision as ours. That is silly, since what we obviously want + * is for the algorithm to operate on the best available precision + * regardless of who runs it. Since the above calculation may + * result in significant variance at 1e-12, rounding would be bogus. + */ + +#ifdef NEG_DEBUG + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00688) + "Variant: file=%s type=%s lang=%s sourceq=%1.3f " + "mimeq=%1.3f langq=%1.3f charq=%1.3f encq=%1.3f " + "q=%1.5f definite=%d", + (variant->file_name ? variant->file_name : ""), + (variant->mime_type ? variant->mime_type : ""), + (variant->content_languages + ? apr_array_pstrcat(neg->pool, variant->content_languages, ',') + : ""), + variant->source_quality, + variant->mime_type_quality, + variant->lang_quality, + variant->charset_quality, + variant->encoding_quality, + q, + variant->definite); +#endif + + if (q <= 0.0f) { + return 0; + } + if (q > bestq) { + *p_bestq = q; + return 1; + } + if (q == bestq) { + /* If the best variant's encoding is of lesser quality than + * this variant, then we prefer this variant + */ + if (variant->encoding_quality > best->encoding_quality) { + *p_bestq = q; + return 1; + } + } + return 0; +} + +/* Negotiation algorithm as used by previous versions of Apache + * (just about). + */ + +static int is_variant_better(negotiation_state *neg, var_rec *variant, + var_rec *best, float *p_bestq) +{ + float bestq = *p_bestq, q; + int levcmp; + + /* For non-transparent negotiation, server can choose how + * to handle the negotiation. We'll use the following in + * order: content-type, language, content-type level, charset, + * content encoding, content length. + * + * For each check, we have three possible outcomes: + * This variant is worse than current best: return 0 + * This variant is better than the current best: + * assign this variant's q to *p_bestq, and return 1 + * This variant is just as desirable as the current best: + * drop through to the next test. + * + * This code is written in this long-winded way to allow future + * customisation, either by the addition of additional + * checks, or to allow the order of the checks to be determined + * by configuration options (e.g. we might prefer to check + * language quality _before_ content type). + */ + + /* First though, eliminate this variant if it is not + * acceptable by type, charset, encoding or language. + */ + +#ifdef NEG_DEBUG + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00689) + "Variant: file=%s type=%s lang=%s sourceq=%1.3f " + "mimeq=%1.3f langq=%1.3f langidx=%d charq=%1.3f encq=%1.3f ", + (variant->file_name ? variant->file_name : ""), + (variant->mime_type ? variant->mime_type : ""), + (variant->content_languages + ? apr_array_pstrcat(neg->pool, variant->content_languages, ',') + : ""), + variant->source_quality, + variant->mime_type_quality, + variant->lang_quality, + variant->lang_index, + variant->charset_quality, + variant->encoding_quality); +#endif + + if (variant->encoding_quality == 0.0f || + variant->lang_quality == 0.0f || + variant->source_quality == 0.0f || + variant->charset_quality == 0.0f || + variant->mime_type_quality == 0.0f) { + return 0; /* don't consider unacceptables */ + } + + q = variant->mime_type_quality * variant->source_quality; + if (q == 0.0 || q < bestq) { + return 0; + } + if (q > bestq || !best) { + *p_bestq = q; + return 1; + } + + /* language */ + if (variant->lang_quality < best->lang_quality) { + return 0; + } + if (variant->lang_quality > best->lang_quality) { + *p_bestq = q; + return 1; + } + + /* if language qualities were equal, try the LanguagePriority stuff */ + if (best->lang_index != -1 && + (variant->lang_index == -1 || variant->lang_index > best->lang_index)) { + return 0; + } + if (variant->lang_index != -1 && + (best->lang_index == -1 || variant->lang_index < best->lang_index)) { + *p_bestq = q; + return 1; + } + + /* content-type level (sometimes used with text/html, though we + * support it on other types too) + */ + levcmp = level_cmp(variant, best); + if (levcmp == -1) { + return 0; + } + if (levcmp == 1) { + *p_bestq = q; + return 1; + } + + /* charset */ + if (variant->charset_quality < best->charset_quality) { + return 0; + } + /* If the best variant's charset is ISO-8859-1 and this variant has + * the same charset quality, then we prefer this variant + */ + + if (variant->charset_quality > best->charset_quality || + ((variant->content_charset != NULL && + *variant->content_charset != '\0' && + strcmp(variant->content_charset, "iso-8859-1") != 0) && + (best->content_charset == NULL || + *best->content_charset == '\0' || + strcmp(best->content_charset, "iso-8859-1") == 0))) { + *p_bestq = q; + return 1; + } + + /* Prefer the highest value for encoding_quality. + */ + if (variant->encoding_quality < best->encoding_quality) { + return 0; + } + if (variant->encoding_quality > best->encoding_quality) { + *p_bestq = q; + return 1; + } + + /* content length if all else equal */ + if (find_content_length(neg, variant) >= find_content_length(neg, best)) { + return 0; + } + + /* ok, to get here means every thing turned out equal, except + * we have a shorter content length, so use this variant + */ + *p_bestq = q; + return 1; +} + +/* figure out, whether a variant is in a specific language + * it returns also false, if the variant has no language. + */ +static int variant_has_language(var_rec *variant, const char *lang) +{ + /* fast exit */ + if ( !lang + || !variant->content_languages) { + return 0; + } + + if (ap_array_str_contains(variant->content_languages, lang)) { + return 1; + } + + return 0; +} + +/* check for environment variables 'no-gzip' and + * 'gzip-only-text/html' to get a behaviour similar + * to mod_deflate + */ +static int discard_variant_by_env(var_rec *variant, int discard) +{ + if ( is_identity_encoding(variant->content_encoding) + || !strcmp(variant->content_encoding, "identity")) { + return 0; + } + + return ( (discard == DISCARD_ALL_ENCODINGS) + || (discard == DISCARD_ALL_BUT_HTML + && (!variant->mime_type + || strncmp(variant->mime_type, "text/html", 9)))); +} + +static int best_match(negotiation_state *neg, var_rec **pbest) +{ + int j; + var_rec *best; + float bestq = 0.0f; + enum algorithm_results algorithm_result; + int may_discard = 0; + + var_rec *avail_recs = (var_rec *) neg->avail_vars->elts; + + /* fetch request dependent variables + * prefer-language: prefer a certain language. + */ + const char *preferred_language = apr_table_get(neg->r->subprocess_env, + "prefer-language"); + + /* no-gzip: do not send encoded documents */ + if (apr_table_get(neg->r->subprocess_env, "no-gzip")) { + may_discard = DISCARD_ALL_ENCODINGS; + } + + /* gzip-only-text/html: send encoded documents only + * if they are text/html. (no-gzip has a higher priority). + */ + else { + const char *env_value = apr_table_get(neg->r->subprocess_env, + "gzip-only-text/html"); + + if (env_value && !strcmp(env_value, "1")) { + may_discard = DISCARD_ALL_BUT_HTML; + } + } + + set_default_lang_quality(neg); + + /* + * Find the 'best' variant + * We run the loop possibly twice: if "prefer-language" + * environment variable is set but we did not find an appropriate + * best variant. In that case forget the preferred language and + * negotiate over all variants. + */ + + do { + best = NULL; + + for (j = 0; j < neg->avail_vars->nelts; ++j) { + var_rec *variant = &avail_recs[j]; + + /* if this variant is encoded somehow and there are special + * variables set, we do not negotiate it. see above. + */ + if ( may_discard + && discard_variant_by_env(variant, may_discard)) { + continue; + } + + /* if a language is preferred, but the current variant + * is not in that language, then drop it for now + */ + if ( preferred_language + && !variant_has_language(variant, preferred_language)) { + continue; + } + + /* Find all the relevant 'quality' values from the + * Accept... headers, and store in the variant. This also + * prepares for sending an Alternates header etc so we need to + * do it even if we do not actually plan to find a best + * variant. + */ + set_accept_quality(neg, variant); + /* accept the preferred language, even when it's not listed within + * the Accept-Language header + */ + if (preferred_language) { + variant->lang_quality = 1.0f; + variant->definite = 1; + } + else { + set_language_quality(neg, variant); + } + set_encoding_quality(neg, variant); + set_charset_quality(neg, variant); + + /* Only do variant selection if we may actually choose a + * variant for the client + */ + if (neg->may_choose) { + + /* Now find out if this variant is better than the current + * best, either using the RVSA/1.0 algorithm, or Apache's + * internal server-driven algorithm. Presumably other + * server-driven algorithms are possible, and could be + * implemented here. + */ + + if (neg->use_rvsa) { + if (is_variant_better_rvsa(neg, variant, best, &bestq)) { + best = variant; + } + } + else { + if (is_variant_better(neg, variant, best, &bestq)) { + best = variant; + } + } + } + } + + /* We now either have a best variant, or no best variant */ + + if (neg->use_rvsa) { + /* calculate result for RVSA/1.0 algorithm: + * only a choice response if the best variant has q>0 + * and is definite + */ + algorithm_result = (best && best->definite) && (bestq > 0) ? + alg_choice : alg_list; + } + else { + /* calculate result for Apache negotiation algorithm */ + algorithm_result = bestq > 0 ? alg_choice : alg_list; + } + + /* run the loop again, if the "prefer-language" got no clear result */ + if (preferred_language && (!best || algorithm_result != alg_choice)) { + preferred_language = NULL; + continue; + } + + break; + } while (1); + + /* Returning a choice response with a non-neighboring variant is a + * protocol security error in TCN (see rfc2295). We do *not* + * verify here that the variant and URI are neighbors, even though + * we may return alg_choice. We depend on the environment (the + * caller) to only declare the resource transparently negotiable if + * all variants are neighbors. + */ + *pbest = best; + return algorithm_result; +} + +/* Sets response headers for a negotiated response. + * neg->is_transparent determines whether a transparently negotiated + * response or a plain `server driven negotiation' response is + * created. Applicable headers are Alternates, Vary, and TCN. + * + * The Vary header we create is sometimes longer than is required for + * the correct caching of negotiated results by HTTP/1.1 caches. For + * example if we have 3 variants x.html, x.ps.en and x.ps.nl, and if + * the Accept: header assigns a 0 quality to .ps, then the results of + * the two server-side negotiation algorithms we currently implement + * will never depend on Accept-Language so we could return `Vary: + * negotiate, accept' instead of the longer 'Vary: negotiate, accept, + * accept-language' which the code below will return. A routine for + * computing the exact minimal Vary header would be a huge pain to code + * and maintain though, especially because we need to take all possible + * twiddles in the server-side negotiation algorithms into account. + */ +static void set_neg_headers(request_rec *r, negotiation_state *neg, + int alg_result) +{ + apr_table_t *hdrs; + var_rec *avail_recs = (var_rec *) neg->avail_vars->elts; + const char *sample_type = NULL; + const char *sample_language = NULL; + const char *sample_encoding = NULL; + const char *sample_charset = NULL; + char *lang; + char *qstr; + apr_off_t len; + apr_array_header_t *arr; + int max_vlist_array = (neg->avail_vars->nelts * 21); + int first_variant = 1; + int vary_by_type = 0; + int vary_by_language = 0; + int vary_by_charset = 0; + int vary_by_encoding = 0; + int j; + + /* In order to avoid O(n^2) memory copies in building Alternates, + * we preallocate a apr_table_t with the maximum substrings possible, + * fill it with the variant list, and then concatenate the entire array. + * Note that if you change the number of substrings pushed, you also + * need to change the calculation of max_vlist_array above. + */ + if (neg->send_alternates && neg->avail_vars->nelts) + arr = apr_array_make(r->pool, max_vlist_array, sizeof(char *)); + else + arr = NULL; + + /* Put headers into err_headers_out, since send_http_header() + * outputs both headers_out and err_headers_out. + */ + hdrs = r->err_headers_out; + + for (j = 0; j < neg->avail_vars->nelts; ++j) { + var_rec *variant = &avail_recs[j]; + + if (variant->content_languages && variant->content_languages->nelts) { + lang = apr_array_pstrcat(r->pool, variant->content_languages, ','); + } + else { + lang = NULL; + } + + /* Calculate Vary by looking for any difference between variants */ + + if (first_variant) { + sample_type = variant->mime_type; + sample_charset = variant->content_charset; + sample_language = lang; + sample_encoding = variant->content_encoding; + } + else { + if (!vary_by_type && + strcmp(sample_type ? sample_type : "", + variant->mime_type ? variant->mime_type : "")) { + vary_by_type = 1; + } + if (!vary_by_charset && + strcmp(sample_charset ? sample_charset : "", + variant->content_charset ? + variant->content_charset : "")) { + vary_by_charset = 1; + } + if (!vary_by_language && + strcmp(sample_language ? sample_language : "", + lang ? lang : "")) { + vary_by_language = 1; + } + if (!vary_by_encoding && + strcmp(sample_encoding ? sample_encoding : "", + variant->content_encoding ? + variant->content_encoding : "")) { + vary_by_encoding = 1; + } + } + first_variant = 0; + + if (!neg->send_alternates) + continue; + + /* Generate the string components for this Alternates entry */ + + *((const char **) apr_array_push(arr)) = "{\""; + *((const char **) apr_array_push(arr)) = ap_escape_path_segment(r->pool, variant->file_name); + *((const char **) apr_array_push(arr)) = "\" "; + + qstr = (char *) apr_palloc(r->pool, 6); + apr_snprintf(qstr, 6, "%1.3f", variant->source_quality); + + /* Strip trailing zeros (saves those valuable network bytes) */ + if (qstr[4] == '0') { + qstr[4] = '\0'; + if (qstr[3] == '0') { + qstr[3] = '\0'; + if (qstr[2] == '0') { + qstr[1] = '\0'; + } + } + } + *((const char **) apr_array_push(arr)) = qstr; + + if (variant->mime_type && *variant->mime_type) { + *((const char **) apr_array_push(arr)) = " {type "; + *((const char **) apr_array_push(arr)) = variant->mime_type; + *((const char **) apr_array_push(arr)) = "}"; + } + if (variant->content_charset && *variant->content_charset) { + *((const char **) apr_array_push(arr)) = " {charset "; + *((const char **) apr_array_push(arr)) = variant->content_charset; + *((const char **) apr_array_push(arr)) = "}"; + } + if (lang) { + *((const char **) apr_array_push(arr)) = " {language "; + *((const char **) apr_array_push(arr)) = lang; + *((const char **) apr_array_push(arr)) = "}"; + } + if (variant->content_encoding && *variant->content_encoding) { + /* Strictly speaking, this is non-standard, but so is TCN */ + + *((const char **) apr_array_push(arr)) = " {encoding "; + *((const char **) apr_array_push(arr)) = variant->content_encoding; + *((const char **) apr_array_push(arr)) = "}"; + } + + /* Note that the Alternates specification (in rfc2295) does + * not require that we include {length x}, so we could omit it + * if determining the length is too expensive. We currently + * always include it though. + * + * If the variant is a CGI script, find_content_length would + * return the length of the script, not the output it + * produces, so we check for the presence of a handler and if + * there is one we don't add a length. + * + * XXX: TODO: This check does not detect a CGI script if we + * get the variant from a type map. This needs to be fixed + * (without breaking things if the type map specifies a + * content-length, which currently leads to the correct result). + */ + if (!(variant->sub_req && variant->sub_req->handler) + && (len = find_content_length(neg, variant)) >= 0) { + + *((const char **) apr_array_push(arr)) = " {length "; + *((const char **) apr_array_push(arr)) = apr_off_t_toa(r->pool, + len); + *((const char **) apr_array_push(arr)) = "}"; + } + + *((const char **) apr_array_push(arr)) = "}"; + *((const char **) apr_array_push(arr)) = ", "; /* trimmed below */ + } + + if (neg->send_alternates && neg->avail_vars->nelts) { + arr->nelts--; /* remove last comma */ + apr_table_mergen(hdrs, "Alternates", + apr_array_pstrcat(r->pool, arr, '\0')); + } + + if (neg->is_transparent || vary_by_type || vary_by_language || + vary_by_charset || vary_by_encoding) { + + apr_table_mergen(hdrs, "Vary", 2 + apr_pstrcat(r->pool, + neg->is_transparent ? ", negotiate" : "", + vary_by_type ? ", accept" : "", + vary_by_language ? ", accept-language" : "", + vary_by_charset ? ", accept-charset" : "", + vary_by_encoding ? ", accept-encoding" : "", NULL)); + } + + if (neg->is_transparent) { /* Create TCN response header */ + apr_table_setn(hdrs, "TCN", + alg_result == alg_list ? "list" : "choice"); + } +} + +/********************************************************************** + * + * Return an HTML list of variants. This is output as part of the + * choice response or 406 status body. + */ + +static char *make_variant_list(request_rec *r, negotiation_state *neg) +{ + apr_array_header_t *arr; + int i; + int max_vlist_array = (neg->avail_vars->nelts * 15) + 2; + + /* In order to avoid O(n^2) memory copies in building the list, + * we preallocate a apr_table_t with the maximum substrings possible, + * fill it with the variant list, and then concatenate the entire array. + */ + arr = apr_array_make(r->pool, max_vlist_array, sizeof(char *)); + + *((const char **) apr_array_push(arr)) = "Available variants:\n<ul>\n"; + + for (i = 0; i < neg->avail_vars->nelts; ++i) { + var_rec *variant = &((var_rec *) neg->avail_vars->elts)[i]; + const char *filename = variant->file_name ? variant->file_name : ""; + apr_array_header_t *languages = variant->content_languages; + const char *description = variant->description + ? variant->description + : ""; + + /* The format isn't very neat, and it would be nice to make + * the tags human readable (eg replace 'language en' with 'English'). + * Note that if you change the number of substrings pushed, you also + * need to change the calculation of max_vlist_array above. + */ + *((const char **) apr_array_push(arr)) = "<li><a href=\""; + *((const char **) apr_array_push(arr)) = ap_escape_path_segment(r->pool, filename); + *((const char **) apr_array_push(arr)) = "\">"; + *((const char **) apr_array_push(arr)) = ap_escape_html(r->pool, filename); + *((const char **) apr_array_push(arr)) = "</a> "; + *((const char **) apr_array_push(arr)) = description; + + if (variant->mime_type && *variant->mime_type) { + *((const char **) apr_array_push(arr)) = ", type "; + *((const char **) apr_array_push(arr)) = variant->mime_type; + } + if (languages && languages->nelts) { + *((const char **) apr_array_push(arr)) = ", language "; + *((const char **) apr_array_push(arr)) = apr_array_pstrcat(r->pool, + languages, ','); + } + if (variant->content_charset && *variant->content_charset) { + *((const char **) apr_array_push(arr)) = ", charset "; + *((const char **) apr_array_push(arr)) = variant->content_charset; + } + if (variant->content_encoding) { + *((const char **) apr_array_push(arr)) = ", encoding "; + *((const char **) apr_array_push(arr)) = variant->content_encoding; + } + *((const char **) apr_array_push(arr)) = "</li>\n"; + } + *((const char **) apr_array_push(arr)) = "</ul>\n"; + + return apr_array_pstrcat(r->pool, arr, '\0'); +} + +static void store_variant_list(request_rec *r, negotiation_state *neg) +{ + if (r->main == NULL) { + apr_table_setn(r->notes, "variant-list", make_variant_list(r, neg)); + } + else { + apr_table_setn(r->main->notes, "variant-list", + make_variant_list(r->main, neg)); + } +} + +/* Called if we got a "Choice" response from the variant selection algorithm. + * It checks the result of the chosen variant to see if it + * is itself negotiated (if so, return error HTTP_VARIANT_ALSO_VARIES). + * Otherwise, add the appropriate headers to the current response. + */ + +static int setup_choice_response(request_rec *r, negotiation_state *neg, + var_rec *variant) +{ + request_rec *sub_req; + const char *sub_vary; + + if (!variant->sub_req) { + int status; + + sub_req = ap_sub_req_lookup_file(variant->file_name, r, r->output_filters); + status = sub_req->status; + + if (status != HTTP_OK && + !apr_table_get(sub_req->err_headers_out, "TCN")) { + ap_destroy_sub_req(sub_req); + return status; + } + variant->sub_req = sub_req; + } + else { + sub_req = variant->sub_req; + } + + /* The variant selection algorithm told us to return a "Choice" + * response. This is the normal variant response, with + * some extra headers. First, ensure that the chosen + * variant did or will not itself engage in transparent negotiation. + * If not, set the appropriate headers, and fall through to + * the normal variant handling + */ + + /* This catches the error that a transparent type map selects a + * transparent multiviews resource as the best variant. + * + * XXX: We do not signal an error if a transparent type map + * selects a _non_transparent multiviews resource as the best + * variant, because we can generate a legal negotiation response + * in this case. In this case, the vlist_validator of the + * nontransparent subrequest will be lost however. This could + * lead to cases in which a change in the set of variants or the + * negotiation algorithm of the nontransparent resource is never + * propagated up to a HTTP/1.1 cache which interprets Vary. To be + * completely on the safe side we should return HTTP_VARIANT_ALSO_VARIES + * for this type of recursive negotiation too. + */ + if (neg->is_transparent && + apr_table_get(sub_req->err_headers_out, "TCN")) { + return HTTP_VARIANT_ALSO_VARIES; + } + + /* This catches the error that a transparent type map recursively + * selects, as the best variant, another type map which itself + * causes transparent negotiation to be done. + * + * XXX: Actually, we catch this error by catching all cases of + * type map recursion. There are some borderline recursive type + * map arrangements which would not produce transparent + * negotiation protocol errors or lack of cache propagation + * problems, but such arrangements are very hard to detect at this + * point in the control flow, so we do not bother to single them + * out. + * + * Recursive type maps imply a recursive arrangement of negotiated + * resources which is visible to outside clients, and this is not + * supported by the transparent negotiation caching protocols, so + * if we are to have generic support for recursive type maps, we + * have to create some configuration setting which makes all type + * maps non-transparent when recursion is enabled. Also, if we + * want recursive type map support which ensures propagation of + * type map changes into HTTP/1.1 caches that handle Vary, we + * would have to extend the current mechanism for generating + * variant list validators. + */ + if (sub_req->handler && strcmp(sub_req->handler, "type-map") == 0) { + return HTTP_VARIANT_ALSO_VARIES; + } + + /* This adds an appropriate Variant-Vary header if the subrequest + * is a multiviews resource. + * + * XXX: TODO: Note that this does _not_ handle any Vary header + * returned by a CGI if sub_req is a CGI script, because we don't + * see that Vary header yet at this point in the control flow. + * This won't cause any cache consistency problems _unless_ the + * CGI script also returns a Cache-Control header marking the + * response as cacheable. This needs to be fixed, also there are + * problems if a CGI returns an Etag header which also need to be + * fixed. + */ + if ((sub_vary = apr_table_get(sub_req->err_headers_out, "Vary")) != NULL) { + apr_table_setn(r->err_headers_out, "Variant-Vary", sub_vary); + + /* Move the subreq Vary header into the main request to + * prevent having two Vary headers in the response, which + * would be legal but strange. + */ + apr_table_setn(r->err_headers_out, "Vary", sub_vary); + apr_table_unset(sub_req->err_headers_out, "Vary"); + } + + apr_table_setn(r->err_headers_out, "Content-Location", + ap_escape_path_segment(r->pool, variant->file_name)); + + set_neg_headers(r, neg, alg_choice); /* add Alternates and Vary */ + + /* Still to do by caller: add Expires */ + + return 0; +} + +/**************************************************************** + * + * Executive... + */ + +static int do_negotiation(request_rec *r, negotiation_state *neg, + var_rec **bestp, int prefer_scripts) +{ + var_rec *avail_recs = (var_rec *) neg->avail_vars->elts; + int alg_result; /* result of variant selection algorithm */ + int res; + int j; + + /* Decide if resource is transparently negotiable */ + + /* GET or HEAD? (HEAD has same method number as GET) */ + if (r->method_number == M_GET) { + + /* maybe this should be configurable, see also the comment + * about recursive type maps in setup_choice_response() + */ + neg->is_transparent = 1; + + /* We can't be transparent if we are a map file in the middle + * of the request URI. + */ + if (r->path_info && *r->path_info) + neg->is_transparent = 0; + + for (j = 0; j < neg->avail_vars->nelts; ++j) { + var_rec *variant = &avail_recs[j]; + + /* We can't be transparent, because of internal + * assumptions in best_match(), if there is a + * non-neighboring variant. We can have a non-neighboring + * variant when processing a type map. + */ + if (ap_strchr_c(variant->file_name, '/')) + neg->is_transparent = 0; + + /* We can't be transparent, because of the behavior + * of variant typemap bodies. + */ + if (variant->body) { + neg->is_transparent = 0; + } + } + } + + if (neg->is_transparent) { + parse_negotiate_header(r, neg); + } + else { /* configure negotiation on non-transparent resource */ + neg->may_choose = 1; + } + + maybe_add_default_accepts(neg, prefer_scripts); + + alg_result = best_match(neg, bestp); + + /* alg_result is one of + * alg_choice: a best variant is chosen + * alg_list: no best variant is chosen + */ + + if (alg_result == alg_list) { + /* send a list response or HTTP_NOT_ACCEPTABLE error response */ + + neg->send_alternates = 1; /* always include Alternates header */ + set_neg_headers(r, neg, alg_result); + store_variant_list(r, neg); + + if (neg->is_transparent && neg->ua_supports_trans) { + /* XXX todo: expires? cachability? */ + + /* Some HTTP/1.0 clients are known to choke when they get + * a 300 (multiple choices) response without a Location + * header. However the 300 code response we are about + * to generate will only reach 1.0 clients which support + * transparent negotiation, and they should be OK. The + * response should never reach older 1.0 clients, even if + * we have CacheNegotiatedDocs enabled, because no 1.0 + * proxy cache (we know of) will cache and return 300 + * responses (they certainly won't if they conform to the + * HTTP/1.0 specification). + */ + return HTTP_MULTIPLE_CHOICES; + } + + if (!*bestp) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00690) + "no acceptable variant: %s", r->filename); + return HTTP_NOT_ACCEPTABLE; + } + } + + /* Variant selection chose a variant */ + + /* XXX todo: merge the two cases in the if statement below */ + if (neg->is_transparent) { + + if ((res = setup_choice_response(r, neg, *bestp)) != 0) { + return res; /* return if error */ + } + } + else { + set_neg_headers(r, neg, alg_result); + } + + /* Make sure caching works - Vary should handle HTTP/1.1, but for + * HTTP/1.0, we can't allow caching at all. + */ + + /* XXX: Note that we only set r->no_cache to 1, which causes + * Expires: <now> to be added, when responding to a HTTP/1.0 + * client. If we return the response to a 1.1 client, we do not + * add Expires <now>, because doing so would degrade 1.1 cache + * performance by preventing re-use of the response without prior + * revalidation. On the other hand, if the 1.1 client is a proxy + * which was itself contacted by a 1.0 client, or a proxy cache + * which can be contacted later by 1.0 clients, then we currently + * rely on this 1.1 proxy to add the Expires: <now> when it + * forwards the response. + * + * XXX: TODO: Find out if the 1.1 spec requires proxies and + * tunnels to add Expires: <now> when forwarding the response to + * 1.0 clients. I (kh) recall it is rather vague on this point. + * Testing actual 1.1 proxy implementations would also be nice. If + * Expires: <now> is not added by proxies then we need to always + * include Expires: <now> ourselves to ensure correct caching, but + * this would degrade HTTP/1.1 cache efficiency unless we also add + * Cache-Control: max-age=N, which we currently don't. + * + * Roy: No, we are not going to screw over HTTP future just to + * ensure that people who can't be bothered to upgrade their + * clients will always receive perfect server-side negotiation. + * Hell, those clients are sending bogus accept headers anyway. + * + * Manual setting of cache-control/expires always overrides this + * automated kluge, on purpose. + */ + + if ((!do_cache_negotiated_docs(r->server) + && (r->proto_num < HTTP_VERSION(1,1))) + && neg->count_multiviews_variants != 1) { + r->no_cache = 1; + } + + return OK; +} + +static int handle_map_file(request_rec *r) +{ + negotiation_state *neg; + apr_file_t *map; + var_rec *best; + int res; + char *udir; + const char *new_req; + + if (strcmp(r->handler, MAP_FILE_MAGIC_TYPE) && strcmp(r->handler, "type-map")) { + return DECLINED; + } + + neg = parse_accept_headers(r); + if ((res = read_type_map(&map, neg, r))) { + return res; + } + + res = do_negotiation(r, neg, &best, 0); + if (res != 0) { + return res; + } + + if (best->body) + { + conn_rec *c = r->connection; + apr_bucket_brigade *bb; + apr_bucket *e; + + ap_allow_standard_methods(r, REPLACE_ALLOW, M_GET, M_OPTIONS, + M_POST, -1); + /* XXX: ? + * if (r->method_number == M_OPTIONS) { + * return ap_send_http_options(r); + *} + */ + if (r->method_number != M_GET && r->method_number != M_POST) { + return HTTP_METHOD_NOT_ALLOWED; + } + + /* ### These may be implemented by adding some 'extra' info + * of the file offset onto the etag + * ap_update_mtime(r, r->finfo.mtime); + * ap_set_last_modified(r); + * ap_set_etag(r); + */ + ap_set_accept_ranges(r); + ap_set_content_length(r, best->bytes); + + /* set MIME type and charset as negotiated */ + if (best->mime_type && *best->mime_type) { + if (best->content_charset && *best->content_charset) { + ap_set_content_type(r, apr_pstrcat(r->pool, + best->mime_type, + "; charset=", + best->content_charset, + NULL)); + } + else { + ap_set_content_type(r, apr_pstrdup(r->pool, best->mime_type)); + } + } + + /* set Content-language(s) as negotiated */ + if (best->content_languages && best->content_languages->nelts) { + r->content_languages = apr_array_copy(r->pool, + best->content_languages); + } + + /* set Content-Encoding as negotiated */ + if (best->content_encoding && *best->content_encoding) { + r->content_encoding = apr_pstrdup(r->pool, + best->content_encoding); + } + + if ((res = ap_meets_conditions(r)) != OK) { + return res; + } + + if ((res = ap_discard_request_body(r)) != OK) { + return res; + } + bb = apr_brigade_create(r->pool, c->bucket_alloc); + + apr_brigade_insert_file(bb, map, best->body, best->bytes, r->pool); + + e = apr_bucket_eos_create(c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + + return ap_pass_brigade_fchk(r, bb, NULL); + } + + if (r->path_info && *r->path_info) { + /* remove any path_info from the end of the uri before trying + * to change the filename. r->path_info from the original + * request is passed along on the redirect. + */ + r->uri[ap_find_path_info(r->uri, r->path_info)] = '\0'; + } + udir = ap_make_dirstr_parent(r->pool, r->uri); + udir = ap_escape_uri(r->pool, udir); + if (r->args) { + if (r->path_info) { + new_req = apr_pstrcat(r->pool, udir, best->file_name, + r->path_info, "?", r->args, NULL); + } + else { + new_req = apr_pstrcat(r->pool, udir, best->file_name, + "?", r->args, NULL); + } + } + else { + new_req = apr_pstrcat(r->pool, udir, best->file_name, + r->path_info, NULL); + } + ap_internal_redirect(new_req, r); + return OK; +} + +static int handle_multi(request_rec *r) +{ + negotiation_state *neg; + var_rec *best, *avail_recs; + request_rec *sub_req; + int res; + int j; + + if (r->finfo.filetype != APR_NOFILE + || !(ap_allow_options(r) & OPT_MULTI)) { + return DECLINED; + } + + neg = parse_accept_headers(r); + + if ((res = read_types_multi(neg))) { + return_from_multi: + /* free all allocated memory from subrequests */ + avail_recs = (var_rec *) neg->avail_vars->elts; + for (j = 0; j < neg->avail_vars->nelts; ++j) { + var_rec *variant = &avail_recs[j]; + if (variant->sub_req) { + ap_destroy_sub_req(variant->sub_req); + } + } + return res; + } + if (neg->avail_vars->nelts == 0) { + return DECLINED; + } + + res = do_negotiation(r, neg, &best, + (r->method_number != M_GET) || r->args || + (r->path_info && *r->path_info)); + if (res != 0) + goto return_from_multi; + + if (!(sub_req = best->sub_req)) { + /* We got this out of a map file, so we don't actually have + * a sub_req structure yet. Get one now. + */ + + sub_req = ap_sub_req_lookup_file(best->file_name, r, r->output_filters); + if (sub_req->status != HTTP_OK) { + res = sub_req->status; + ap_destroy_sub_req(sub_req); + goto return_from_multi; + } + } + if (sub_req->args == NULL) { + sub_req->args = r->args; + } + + /* now do a "fast redirect" ... promotes the sub_req into the main req */ + ap_internal_fast_redirect(sub_req, r); + + /* give no advise for time on this subrequest. Perhaps we + * should tally the last mtime among all variants, and date + * the most recent, but that could confuse the proxies. + */ + r->mtime = 0; + + /* clean up all but our favorite variant, since that sub_req + * is now merged into the main request! + */ + avail_recs = (var_rec *) neg->avail_vars->elts; + for (j = 0; j < neg->avail_vars->nelts; ++j) { + var_rec *variant = &avail_recs[j]; + if (variant != best && variant->sub_req) { + ap_destroy_sub_req(variant->sub_req); + } + } + return OK; +} + +/********************************************************************** + * There is a problem with content-encoding, as some clients send and + * expect an x- token (e.g. x-gzip) while others expect the plain token + * (i.e. gzip). To try and deal with this as best as possible we do + * the following: if the client sent an Accept-Encoding header and it + * contains a plain token corresponding to the content encoding of the + * response, then set content encoding using the plain token. Else if + * the A-E header contains the x- token use the x- token in the C-E + * header. Else don't do anything. + * + * Note that if no A-E header was sent, or it does not contain a token + * compatible with the final content encoding, then the token in the + * C-E header will be whatever was specified in the AddEncoding + * directive. + */ +static int fix_encoding(request_rec *r) +{ + const char *enc = r->content_encoding; + char *x_enc = NULL; + apr_array_header_t *accept_encodings; + accept_rec *accept_recs; + int i; + + if (!enc || !*enc) { + return DECLINED; + } + + if (enc[0] == 'x' && enc[1] == '-') { + enc += 2; + } + + if ((accept_encodings = do_header_line(r->pool, + apr_table_get(r->headers_in, "Accept-Encoding"))) == NULL) { + return DECLINED; + } + + accept_recs = (accept_rec *) accept_encodings->elts; + + for (i = 0; i < accept_encodings->nelts; ++i) { + char *name = accept_recs[i].name; + + if (!strcmp(name, enc)) { + r->content_encoding = name; + return OK; + } + + if (name[0] == 'x' && name[1] == '-' && !strcmp(name+2, enc)) { + x_enc = name; + } + } + + if (x_enc) { + r->content_encoding = x_enc; + return OK; + } + + return DECLINED; +} + +static void register_hooks(apr_pool_t *p) +{ + ap_hook_fixups(fix_encoding,NULL,NULL,APR_HOOK_MIDDLE); + ap_hook_type_checker(handle_multi,NULL,NULL,APR_HOOK_FIRST); + ap_hook_handler(handle_map_file,NULL,NULL,APR_HOOK_MIDDLE); +} + +AP_DECLARE_MODULE(negotiation) = +{ + STANDARD20_MODULE_STUFF, + create_neg_dir_config, /* dir config creator */ + merge_neg_dir_configs, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + negotiation_cmds, /* command apr_table_t */ + register_hooks /* register hooks */ +}; diff --git a/modules/mappers/mod_negotiation.dep b/modules/mappers/mod_negotiation.dep new file mode 100644 index 0000000..93a58df --- /dev/null +++ b/modules/mappers/mod_negotiation.dep @@ -0,0 +1,58 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_negotiation.mak + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + + +.\mod_negotiation.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_script.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + diff --git a/modules/mappers/mod_negotiation.dsp b/modules/mappers/mod_negotiation.dsp new file mode 100644 index 0000000..4f24316 --- /dev/null +++ b/modules/mappers/mod_negotiation.dsp @@ -0,0 +1,111 @@ +# Microsoft Developer Studio Project File - Name="mod_negotiation" - 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_negotiation - 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_negotiation.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_negotiation.mak" CFG="mod_negotiation - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_negotiation - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_negotiation - 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_negotiation - 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_negotiation_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_negotiation.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_negotiation.so" /d LONG_NAME="negotiation_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_negotiation.so" /base:@..\..\os\win32\BaseAddr.ref,mod_negotiation.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_negotiation.so" /base:@..\..\os\win32\BaseAddr.ref,mod_negotiation.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_negotiation.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_negotiation - 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_negotiation_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_negotiation.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_negotiation.so" /d LONG_NAME="negotiation_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_negotiation.so" /base:@..\..\os\win32\BaseAddr.ref,mod_negotiation.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_negotiation.so" /base:@..\..\os\win32\BaseAddr.ref,mod_negotiation.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_negotiation.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_negotiation - Win32 Release" +# Name "mod_negotiation - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_negotiation.c +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/mappers/mod_negotiation.exp b/modules/mappers/mod_negotiation.exp new file mode 100644 index 0000000..a7c18da --- /dev/null +++ b/modules/mappers/mod_negotiation.exp @@ -0,0 +1 @@ +negotiation_module diff --git a/modules/mappers/mod_negotiation.mak b/modules/mappers/mod_negotiation.mak new file mode 100644 index 0000000..6de4c00 --- /dev/null +++ b/modules/mappers/mod_negotiation.mak @@ -0,0 +1,353 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_negotiation.dsp +!IF "$(CFG)" == "" +CFG=mod_negotiation - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_negotiation - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_negotiation - Win32 Release" && "$(CFG)" != "mod_negotiation - 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_negotiation.mak" CFG="mod_negotiation - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_negotiation - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_negotiation - 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_negotiation - 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_negotiation.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_negotiation.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_negotiation.obj" + -@erase "$(INTDIR)\mod_negotiation.res" + -@erase "$(INTDIR)\mod_negotiation_src.idb" + -@erase "$(INTDIR)\mod_negotiation_src.pdb" + -@erase "$(OUTDIR)\mod_negotiation.exp" + -@erase "$(OUTDIR)\mod_negotiation.lib" + -@erase "$(OUTDIR)\mod_negotiation.pdb" + -@erase "$(OUTDIR)\mod_negotiation.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_negotiation_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_negotiation.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_negotiation.so" /d LONG_NAME="negotiation_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_negotiation.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_negotiation.pdb" /debug /out:"$(OUTDIR)\mod_negotiation.so" /implib:"$(OUTDIR)\mod_negotiation.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_negotiation.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_negotiation.obj" \ + "$(INTDIR)\mod_negotiation.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" + +"$(OUTDIR)\mod_negotiation.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_negotiation.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_negotiation.so" + if exist .\Release\mod_negotiation.so.manifest mt.exe -manifest .\Release\mod_negotiation.so.manifest -outputresource:.\Release\mod_negotiation.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_negotiation - 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_negotiation.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_negotiation.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_negotiation.obj" + -@erase "$(INTDIR)\mod_negotiation.res" + -@erase "$(INTDIR)\mod_negotiation_src.idb" + -@erase "$(INTDIR)\mod_negotiation_src.pdb" + -@erase "$(OUTDIR)\mod_negotiation.exp" + -@erase "$(OUTDIR)\mod_negotiation.lib" + -@erase "$(OUTDIR)\mod_negotiation.pdb" + -@erase "$(OUTDIR)\mod_negotiation.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_negotiation_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_negotiation.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_negotiation.so" /d LONG_NAME="negotiation_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_negotiation.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_negotiation.pdb" /debug /out:"$(OUTDIR)\mod_negotiation.so" /implib:"$(OUTDIR)\mod_negotiation.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_negotiation.so +LINK32_OBJS= \ + "$(INTDIR)\mod_negotiation.obj" \ + "$(INTDIR)\mod_negotiation.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" + +"$(OUTDIR)\mod_negotiation.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_negotiation.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_negotiation.so" + if exist .\Debug\mod_negotiation.so.manifest mt.exe -manifest .\Debug\mod_negotiation.so.manifest -outputresource:.\Debug\mod_negotiation.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_negotiation.dep") +!INCLUDE "mod_negotiation.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_negotiation.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_negotiation - Win32 Release" || "$(CFG)" == "mod_negotiation - Win32 Debug" + +!IF "$(CFG)" == "mod_negotiation - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\mappers" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_negotiation - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\mappers" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ENDIF + +!IF "$(CFG)" == "mod_negotiation - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\mappers" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_negotiation - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\mappers" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ENDIF + +!IF "$(CFG)" == "mod_negotiation - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\mappers" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_negotiation - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\mappers" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\mappers" + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_negotiation - Win32 Release" + + +"$(INTDIR)\mod_negotiation.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_negotiation.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_negotiation.so" /d LONG_NAME="negotiation_module for Apache" $(SOURCE) + + +!ELSEIF "$(CFG)" == "mod_negotiation - Win32 Debug" + + +"$(INTDIR)\mod_negotiation.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_negotiation.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_negotiation.so" /d LONG_NAME="negotiation_module for Apache" $(SOURCE) + + +!ENDIF + +SOURCE=.\mod_negotiation.c + +"$(INTDIR)\mod_negotiation.obj" : $(SOURCE) "$(INTDIR)" + + + +!ENDIF + diff --git a/modules/mappers/mod_rewrite.c b/modules/mappers/mod_rewrite.c new file mode 100644 index 0000000..f93f23f --- /dev/null +++ b/modules/mappers/mod_rewrite.c @@ -0,0 +1,5431 @@ +/* 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. + */ + +/* _ _ _ + * _ __ ___ ___ __| | _ __ _____ ___ __(_) |_ ___ + * | '_ ` _ \ / _ \ / _` | | '__/ _ \ \ /\ / / '__| | __/ _ \ + * | | | | | | (_) | (_| | | | | __/\ V V /| | | | || __/ + * |_| |_| |_|\___/ \__,_|___|_| \___| \_/\_/ |_| |_|\__\___| + * |_____| + * + * URL Rewriting Module + * + * This module uses a rule-based rewriting engine (based on a + * regular-expression parser) to rewrite requested URLs on the fly. + * + * It supports an unlimited number of additional rule conditions (which can + * operate on a lot of variables, even on HTTP headers) for granular + * matching and even external database lookups (either via plain text + * tables, DBM hash files or even external processes) for advanced URL + * substitution. + * + * It operates on the full URLs (including the PATH_INFO part) both in + * per-server context (httpd.conf) and per-dir context (.htaccess) and even + * can generate QUERY_STRING parts on result. The rewriting result finally + * can lead to internal subprocessing, external request redirection or even + * to internal proxy throughput. + * + * This module was originally written in April 1996 and + * gifted exclusively to the The Apache Software Foundation in July 1997 by + * + * Ralf S. Engelschall + * rse engelschall.com + * www.engelschall.com + */ + +#include "apr.h" +#include "apr_strings.h" +#include "apr_hash.h" +#include "apr_user.h" +#include "apr_lib.h" +#include "apr_signal.h" +#include "apr_global_mutex.h" +#include "apr_dbm.h" +#include "apr_dbd.h" + +#include "apr_version.h" +#if !APR_VERSION_AT_LEAST(2,0,0) +#include "apu_version.h" +#endif + +#include "mod_dbd.h" + +#if APR_HAS_THREADS +#include "apr_thread_mutex.h" +#endif + +#define APR_WANT_MEMFUNC +#define APR_WANT_STRFUNC +#define APR_WANT_IOVEC +#include "apr_want.h" + +/* XXX: Do we really need these headers? */ +#if APR_HAVE_UNISTD_H +#include <unistd.h> +#endif +#if APR_HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#if APR_HAVE_STDARG_H +#include <stdarg.h> +#endif +#if APR_HAVE_STDLIB_H +#include <stdlib.h> +#endif +#if APR_HAVE_CTYPE_H +#include <ctype.h> +#endif +#if APR_HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif + +#include "ap_config.h" +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_log.h" +#include "http_protocol.h" +#include "http_ssl.h" +#include "http_vhost.h" +#include "util_mutex.h" + +#include "mod_rewrite.h" +#include "ap_expr.h" + +#include "test_char.h" + +static ap_dbd_t *(*dbd_acquire)(request_rec*) = NULL; +static void (*dbd_prepare)(server_rec*, const char*, const char*) = NULL; +static const char* really_last_key = "rewrite_really_last"; + +/* + * in order to improve performance on running production systems, you + * may strip all rewritelog code entirely from mod_rewrite by using the + * -DREWRITELOG_DISABLED compiler option. + * + * DO NOT USE THIS OPTION FOR PUBLIC BINARY RELEASES. Otherwise YOU are + * responsible for answering all the mod_rewrite questions out there. + */ +/* If logging is limited to APLOG_DEBUG or lower, disable rewrite log, too */ +#ifdef APLOG_MAX_LOGLEVEL +#if APLOG_MAX_LOGLEVEL < APLOG_TRACE1 +#ifndef REWRITELOG_DISABLED +#define REWRITELOG_DISABLED +#endif +#endif +#endif + +#ifndef REWRITELOG_DISABLED + +#define rewritelog(x) do_rewritelog x +#define REWRITELOG_MODE ( APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD ) +#define REWRITELOG_FLAGS ( APR_WRITE | APR_APPEND | APR_CREATE ) + +#else /* !REWRITELOG_DISABLED */ + +#define rewritelog(x) + +#endif /* REWRITELOG_DISABLED */ + +/* remembered mime-type for [T=...] */ +#define REWRITE_FORCED_MIMETYPE_NOTEVAR "rewrite-forced-mimetype" +#define REWRITE_FORCED_HANDLER_NOTEVAR "rewrite-forced-handler" + +#define ENVVAR_SCRIPT_URL "SCRIPT_URL" +#define REDIRECT_ENVVAR_SCRIPT_URL "REDIRECT_" ENVVAR_SCRIPT_URL +#define ENVVAR_SCRIPT_URI "SCRIPT_URI" + +#define CONDFLAG_NONE (1<<0) +#define CONDFLAG_NOCASE (1<<1) +#define CONDFLAG_NOTMATCH (1<<2) +#define CONDFLAG_ORNEXT (1<<3) +#define CONDFLAG_NOVARY (1<<4) + +#define RULEFLAG_NONE (1<<0) +#define RULEFLAG_FORCEREDIRECT (1<<1) +#define RULEFLAG_LASTRULE (1<<2) +#define RULEFLAG_NEWROUND (1<<3) +#define RULEFLAG_CHAIN (1<<4) +#define RULEFLAG_IGNOREONSUBREQ (1<<5) +#define RULEFLAG_NOTMATCH (1<<6) +#define RULEFLAG_PROXY (1<<7) +#define RULEFLAG_PASSTHROUGH (1<<8) +#define RULEFLAG_QSAPPEND (1<<9) +#define RULEFLAG_NOCASE (1<<10) +#define RULEFLAG_NOESCAPE (1<<11) +#define RULEFLAG_NOSUB (1<<12) +#define RULEFLAG_STATUS (1<<13) +#define RULEFLAG_ESCAPEBACKREF (1<<14) +#define RULEFLAG_DISCARDPATHINFO (1<<15) +#define RULEFLAG_QSDISCARD (1<<16) +#define RULEFLAG_END (1<<17) +#define RULEFLAG_ESCAPENOPLUS (1<<18) +#define RULEFLAG_QSLAST (1<<19) +#define RULEFLAG_QSNONE (1<<20) /* programattic only */ +#define RULEFLAG_ESCAPECTLS (1<<21) + +/* return code of the rewrite rule + * the result may be escaped - or not + */ +#define ACTION_NORMAL (1<<0) +#define ACTION_NOESCAPE (1<<1) +#define ACTION_STATUS (1<<2) + + +#define MAPTYPE_TXT (1<<0) +#define MAPTYPE_DBM (1<<1) +#define MAPTYPE_PRG (1<<2) +#define MAPTYPE_INT (1<<3) +#define MAPTYPE_RND (1<<4) +#define MAPTYPE_DBD (1<<5) +#define MAPTYPE_DBD_CACHE (1<<6) + +#define ENGINE_DISABLED (1<<0) +#define ENGINE_ENABLED (1<<1) + +#define OPTION_NONE (1<<0) +#define OPTION_INHERIT (1<<1) +#define OPTION_INHERIT_BEFORE (1<<2) +#define OPTION_NOSLASH (1<<3) +#define OPTION_ANYURI (1<<4) +#define OPTION_MERGEBASE (1<<5) +#define OPTION_INHERIT_DOWN (1<<6) +#define OPTION_INHERIT_DOWN_BEFORE (1<<7) +#define OPTION_IGNORE_INHERIT (1<<8) +#define OPTION_IGNORE_CONTEXT_INFO (1<<9) +#define OPTION_LEGACY_PREFIX_DOCROOT (1<<10) + +#ifndef RAND_MAX +#define RAND_MAX 32767 +#endif + +/* max cookie size in rfc 2109 */ +/* XXX: not used at all. We should do a check somewhere and/or cut the cookie */ +#define MAX_COOKIE_LEN 4096 + +/* max line length (incl.\n) in text rewrite maps */ +#ifndef REWRITE_MAX_TXT_MAP_LINE +#define REWRITE_MAX_TXT_MAP_LINE 1024 +#endif + +/* buffer length for prg rewrite maps */ +#ifndef REWRITE_PRG_MAP_BUF +#define REWRITE_PRG_MAP_BUF 1024 +#endif + +/* for better readbility */ +#define LEFT_CURLY '{' +#define RIGHT_CURLY '}' + +/* + * expansion result items on the stack to save some cycles + * + * (5 == about 2 variables like "foo%{var}bar%{var}baz") + */ +#define SMALL_EXPANSION 5 + +/* + * check that a subrequest won't cause infinite recursion + * + * either not in a subrequest, or in a subrequest + * and URIs aren't NULL and sub/main URIs differ + */ +#define subreq_ok(r) (!r->main || \ + (r->main->uri && r->uri && strcmp(r->main->uri, r->uri))) + +#ifndef REWRITE_MAX_ROUNDS +#define REWRITE_MAX_ROUNDS 32000 +#endif + +/* + * +-------------------------------------------------------+ + * | | + * | Types and Structures + * | | + * +-------------------------------------------------------+ + */ + +typedef struct { + const char *datafile; /* filename for map data files */ + const char *dbmtype; /* dbm type for dbm map data files */ + const char *checkfile; /* filename to check for map existence */ + const char *cachename; /* for cached maps (txt/rnd/dbm) */ + int type; /* the type of the map */ + apr_file_t *fpin; /* in file pointer for program maps */ + apr_file_t *fpout; /* out file pointer for program maps */ + apr_file_t *fperr; /* err file pointer for program maps */ + char *(*func)(request_rec *, /* function pointer for internal maps */ + char *); + char **argv; /* argv of the external rewrite map */ + const char *dbdq; /* SQL SELECT statement for rewritemap */ + const char *checkfile2; /* filename to check for map existence + NULL if only one file */ + const char *user; /* run RewriteMap program as this user */ + const char *group; /* run RewriteMap program as this group */ +} rewritemap_entry; + +/* special pattern types for RewriteCond */ +typedef enum { + CONDPAT_REGEX = 0, + CONDPAT_FILE_EXISTS, + CONDPAT_FILE_SIZE, + CONDPAT_FILE_LINK, + CONDPAT_FILE_DIR, + CONDPAT_FILE_XBIT, + CONDPAT_LU_URL, + CONDPAT_LU_FILE, + CONDPAT_STR_LT, + CONDPAT_STR_LE, + CONDPAT_STR_EQ, + CONDPAT_STR_GT, + CONDPAT_STR_GE, + CONDPAT_INT_LT, + CONDPAT_INT_LE, + CONDPAT_INT_EQ, + CONDPAT_INT_GT, + CONDPAT_INT_GE, + CONDPAT_AP_EXPR +} pattern_type; + +typedef struct { + char *input; /* Input string of RewriteCond */ + char *pattern; /* the RegExp pattern string */ + ap_regex_t *regexp; /* the precompiled regexp */ + ap_expr_info_t *expr; /* the compiled ap_expr */ + int flags; /* Flags which control the match */ + pattern_type ptype; /* pattern type */ + int pskip; /* back-index to display pattern */ +} rewritecond_entry; + +/* single linked list for env vars and cookies */ +typedef struct data_item { + struct data_item *next; + char *data; +} data_item; + +typedef struct { + apr_array_header_t *rewriteconds;/* the corresponding RewriteCond entries */ + char *pattern; /* the RegExp pattern string */ + ap_regex_t *regexp; /* the RegExp pattern compilation */ + char *output; /* the Substitution string */ + int flags; /* Flags which control the substitution */ + char *forced_mimetype; /* forced MIME type of substitution */ + char *forced_handler; /* forced content handler of subst. */ + int forced_responsecode; /* forced HTTP response status */ + data_item *env; /* added environment variables */ + data_item *cookie; /* added cookies */ + int skip; /* number of next rules to skip */ + int maxrounds; /* limit on number of loops with N flag */ + const char *escapes; /* specific backref escapes */ + const char *noescapes; /* specific backref chars not to escape */ +} rewriterule_entry; + +typedef struct { + int state; /* the RewriteEngine state */ + int options; /* the RewriteOption state */ + apr_hash_t *rewritemaps; /* the RewriteMap entries */ + apr_array_header_t *rewriteconds; /* the RewriteCond entries (temp.) */ + apr_array_header_t *rewriterules; /* the RewriteRule entries */ + server_rec *server; /* the corresponding server indicator */ + unsigned int state_set:1; + unsigned int options_set:1; +} rewrite_server_conf; + +typedef struct { + int state; /* the RewriteEngine state */ + int options; /* the RewriteOption state */ + apr_array_header_t *rewriteconds; /* the RewriteCond entries (temp.) */ + apr_array_header_t *rewriterules; /* the RewriteRule entries */ + char *directory; /* the directory where it applies */ + const char *baseurl; /* the base-URL where it applies */ + unsigned int state_set:1; + unsigned int options_set:1; + unsigned int baseurl_set:1; +} rewrite_perdir_conf; + +/* the (per-child) cache structures. + */ +typedef struct cache { + apr_pool_t *pool; + apr_hash_t *maps; +#if APR_HAS_THREADS + apr_thread_mutex_t *lock; +#endif +} cache; + +/* cached maps contain an mtime for the whole map and live in a subpool + * of the cachep->pool. That makes it easy to forget them if necessary. + */ +typedef struct { + apr_time_t mtime; + apr_pool_t *pool; + apr_hash_t *entries; +} cachedmap; + +/* the regex structure for the + * substitution of backreferences + */ +typedef struct backrefinfo { + const char *source; + ap_regmatch_t regmatch[AP_MAX_REG_MATCH]; +} backrefinfo; + +/* single linked list used for + * variable expansion + */ +typedef struct result_list { + struct result_list *next; + apr_size_t len; + const char *string; +} result_list; + +/* context structure for variable lookup and expansion + */ +typedef struct { + request_rec *r; + const char *uri; + const char *vary_this; + const char *vary; + char *perdir; + backrefinfo briRR; + backrefinfo briRC; +} rewrite_ctx; + +/* + * +-------------------------------------------------------+ + * | | + * | static module data + * | | + * +-------------------------------------------------------+ + */ + +/* the global module structure */ +module AP_MODULE_DECLARE_DATA rewrite_module; + +/* rewritemap int: handler function registry */ +static apr_hash_t *mapfunc_hash; + +/* the cache */ +static cache *cachep; + +/* whether proxy module is available or not */ +static int proxy_available; + +/* Locks/Mutexes */ +static int rewrite_lock_needed = 0; +static apr_global_mutex_t *rewrite_mapr_lock_acquire = NULL; +static const char *rewritemap_mutex_type = "rewrite-map"; + +/* Optional functions imported from mod_ssl when loaded: */ +static char *escape_backref(apr_pool_t *p, const char *path, + const char *escapeme, const char *noescapeme, + int flags); + +/* + * +-------------------------------------------------------+ + * | | + * | rewriting logfile support + * | | + * +-------------------------------------------------------+ + */ + +#ifndef REWRITELOG_DISABLED +static void do_rewritelog(request_rec *r, int level, char *perdir, + const char *fmt, ...) + __attribute__((format(printf,4,5))); + +static void do_rewritelog(request_rec *r, int level, char *perdir, + const char *fmt, ...) +{ + char *logline, *text; + const char *rhost, *rname; + int redir; + request_rec *req; + va_list ap; + + if (!APLOG_R_IS_LEVEL(r, APLOG_DEBUG + level)) + return; + + rhost = ap_get_useragent_host(r, REMOTE_NOLOOKUP, NULL); + rname = ap_get_remote_logname(r); + + for (redir=0, req=r; req->prev; req = req->prev) { + ++redir; + } + + va_start(ap, fmt); + text = apr_pvsprintf(r->pool, fmt, ap); + va_end(ap); + + logline = apr_psprintf(r->pool, "%s %s %s [%s/sid#%pp][rid#%pp/%s%s%s] " + "%s%s%s%s", + rhost ? rhost : "UNKNOWN-HOST", + rname ? rname : "-", + r->user ? (*r->user ? r->user : "\"\"") : "-", + ap_get_server_name(r), + (void *)(r->server), + (void *)r, + r->main ? "subreq" : "initial", + redir ? "/redir#" : "", + redir ? apr_itoa(r->pool, redir) : "", + perdir ? "[perdir " : "", + perdir ? perdir : "", + perdir ? "] ": "", + text); + + AP_REWRITE_LOG((uintptr_t)r, level, r->main ? 0 : 1, (char *)ap_get_server_name(r), logline); + + /* Intentional no APLOGNO */ + ap_log_rerror(APLOG_MARK, APLOG_DEBUG + level, 0, r, "%s", logline); + + return; +} +#endif /* !REWRITELOG_DISABLED */ + + +/* + * +-------------------------------------------------------+ + * | | + * | URI and path functions + * | | + * +-------------------------------------------------------+ + */ + +/* return number of chars of the scheme (incl. '://') + * if the URI is absolute (includes a scheme etc.) + * otherwise 0. + * If supportqs is not NULL, we return a whether or not + * the scheme supports a query string or not. + * + * NOTE: If you add new schemes here, please have a + * look at escape_absolute_uri and splitout_queryargs. + * Not every scheme takes query strings and some schemes + * may be handled in a special way. + * + * XXX: we may consider a scheme registry, perhaps with + * appropriate escape callbacks to allow other modules + * to extend mod_rewrite at runtime. + */ +static unsigned is_absolute_uri(char *uri, int *supportsqs) +{ + int dummy, *sqs; + + sqs = (supportsqs ? supportsqs : &dummy); + *sqs = 0; + /* fast exit */ + if (*uri == '/' || strlen(uri) <= 5) { + return 0; + } + + switch (*uri++) { + case 'a': + case 'A': + if (!ap_cstr_casecmpn(uri, "jp://", 5)) { /* ajp:// */ + *sqs = 1; + return 6; + } + break; + + case 'b': + case 'B': + if (!ap_cstr_casecmpn(uri, "alancer://", 10)) { /* balancer:// */ + *sqs = 1; + return 11; + } + break; + + case 'f': + case 'F': + if (!ap_cstr_casecmpn(uri, "tp://", 5)) { /* ftp:// */ + return 6; + } + if (!ap_cstr_casecmpn(uri, "cgi://", 6)) { /* fcgi:// */ + *sqs = 1; + return 7; + } + break; + + case 'g': + case 'G': + if (!ap_cstr_casecmpn(uri, "opher://", 8)) { /* gopher:// */ + return 9; + } + break; + + case 'h': + case 'H': + if (!ap_cstr_casecmpn(uri, "ttp://", 6)) { /* http:// */ + *sqs = 1; + return 7; + } + else if (!ap_cstr_casecmpn(uri, "ttps://", 7)) { /* https:// */ + *sqs = 1; + return 8; + } + else if (!ap_cstr_casecmpn(uri, "2://", 4)) { /* h2:// */ + *sqs = 1; + return 5; + } + else if (!ap_cstr_casecmpn(uri, "2c://", 5)) { /* h2c:// */ + *sqs = 1; + return 6; + } + break; + + case 'l': + case 'L': + if (!ap_cstr_casecmpn(uri, "dap://", 6)) { /* ldap:// */ + return 7; + } + break; + + case 'm': + case 'M': + if (!ap_cstr_casecmpn(uri, "ailto:", 6)) { /* mailto: */ + *sqs = 1; + return 7; + } + break; + + case 'n': + case 'N': + if (!ap_cstr_casecmpn(uri, "ews:", 4)) { /* news: */ + return 5; + } + else if (!ap_cstr_casecmpn(uri, "ntp://", 6)) { /* nntp:// */ + return 7; + } + break; + + case 's': + case 'S': + if (!ap_cstr_casecmpn(uri, "cgi://", 6)) { /* scgi:// */ + *sqs = 1; + return 7; + } + break; + + case 'w': + case 'W': + if (!ap_cstr_casecmpn(uri, "s://", 4)) { /* ws:// */ + *sqs = 1; + return 5; + } + else if (!ap_cstr_casecmpn(uri, "ss://", 5)) { /* wss:// */ + *sqs = 1; + return 6; + } + break; + + case 'u': + case 'U': + if (!ap_cstr_casecmpn(uri, "nix:", 4)) { /* unix: */ + *sqs = 1; + return (uri[4] == '/' && uri[5] == '/') ? 7 : 5; + } + } + + return 0; +} + +static const char c2x_table[] = "0123456789abcdef"; + +static APR_INLINE unsigned char *c2x(unsigned what, unsigned char prefix, + unsigned char *where) +{ +#if APR_CHARSET_EBCDIC + what = apr_xlate_conv_byte(ap_hdrs_to_ascii, (unsigned char)what); +#endif /*APR_CHARSET_EBCDIC*/ + *where++ = prefix; + *where++ = c2x_table[what >> 4]; + *where++ = c2x_table[what & 0xf]; + return where; +} + +/* + * Escapes a backreference in a similar way as php's urlencode does. + * Based on ap_os_escape_path in server/util.c + */ +static char *escape_backref(apr_pool_t *p, const char *path, + const char *escapeme, const char *noescapeme, + int flags) +{ + char *copy = apr_palloc(p, 3 * strlen(path) + 1); + const unsigned char *s = (const unsigned char *)path; + unsigned char *d = (unsigned char *)copy; + int noplus = (flags & RULEFLAG_ESCAPENOPLUS) != 0; + int ctls = (flags & RULEFLAG_ESCAPECTLS) != 0; + unsigned char c; + + while ((c = *s)) { + if (((ctls ? !TEST_CHAR(c, T_VCHAR_OBSTEXT) : !escapeme) + || (escapeme && ap_strchr_c(escapeme, c))) + && (!noescapeme || !ap_strchr_c(noescapeme, c))) { + if (apr_isalnum(c) || c == '_') { + *d++ = c; + } + else if (c == ' ' && !noplus) { + *d++ = '+'; + } + else { + d = c2x(c, '%', d); + } + } + else { + *d++ = c; + } + ++s; + } + *d = '\0'; + return copy; +} + +/* + * escape absolute uri, which may or may not be path oriented. + * So let's handle them differently. + */ +static char *escape_absolute_uri(apr_pool_t *p, char *uri, unsigned scheme) +{ + char *cp; + + /* be safe. + * NULL should indicate elsewhere, that something's wrong + */ + if (!scheme || strlen(uri) < scheme) { + return NULL; + } + + cp = uri + scheme; + + /* scheme with authority part? */ + if (cp[-1] == '/') { + /* skip host part */ + while (*cp && *cp != '/') { + ++cp; + } + + /* nothing after the hostpart. ready! */ + if (!*cp || !*++cp) { + return apr_pstrdup(p, uri); + } + + /* remember the hostname stuff */ + scheme = cp - uri; + + /* special thing for ldap. + * The parts are separated by question marks. From RFC 2255: + * ldapurl = scheme "://" [hostport] ["/" + * [dn ["?" [attributes] ["?" [scope] + * ["?" [filter] ["?" extensions]]]]]] + */ + if (!ap_cstr_casecmpn(uri, "ldap", 4)) { + char *token[5]; + int c = 0; + + token[0] = cp = apr_pstrdup(p, cp); + while (*cp && c < 4) { + if (*cp == '?') { + token[++c] = cp + 1; + *cp = '\0'; + } + ++cp; + } + + return apr_pstrcat(p, apr_pstrndup(p, uri, scheme), + ap_escape_uri(p, token[0]), + (c >= 1) ? "?" : NULL, + (c >= 1) ? ap_escape_uri(p, token[1]) : NULL, + (c >= 2) ? "?" : NULL, + (c >= 2) ? ap_escape_uri(p, token[2]) : NULL, + (c >= 3) ? "?" : NULL, + (c >= 3) ? ap_escape_uri(p, token[3]) : NULL, + (c >= 4) ? "?" : NULL, + (c >= 4) ? ap_escape_uri(p, token[4]) : NULL, + NULL); + } + } + + /* Nothing special here. Apply normal escaping. */ + return apr_pstrcat(p, apr_pstrndup(p, uri, scheme), + ap_escape_uri(p, cp), NULL); +} + +/* + * split out a QUERY_STRING part from + * the current URI string + */ +static void splitout_queryargs(request_rec *r, int flags) +{ + char *q; + int split, skip; + int qsappend = flags & RULEFLAG_QSAPPEND; + int qsdiscard = flags & RULEFLAG_QSDISCARD; + int qslast = flags & RULEFLAG_QSLAST; + + if (flags & RULEFLAG_QSNONE) { + rewritelog((r, 2, NULL, "discarding query string, no parse from substitution")); + r->args = NULL; + return; + } + + /* don't touch, unless it's a scheme for which a query string makes sense. + * See RFC 1738 and RFC 2368. + */ + if ((skip = is_absolute_uri(r->filename, &split)) + && !split) { + r->args = NULL; /* forget the query that's still flying around */ + return; + } + + if (qsdiscard) { + r->args = NULL; /* Discard query string */ + rewritelog((r, 2, NULL, "discarding query string")); + } + + q = qslast ? ap_strrchr(r->filename + skip, '?') : ap_strchr(r->filename + skip, '?'); + + if (q != NULL) { + char *olduri; + apr_size_t len; + + olduri = apr_pstrdup(r->pool, r->filename); + *q++ = '\0'; + if (qsappend) { + if (*q) { + r->args = apr_pstrcat(r->pool, q, "&" , r->args, NULL); + } + } + else { + r->args = apr_pstrdup(r->pool, q); + } + + if (r->args) { + len = strlen(r->args); + + if (!len) { + r->args = NULL; + } + else if (r->args[len-1] == '&') { + r->args[len-1] = '\0'; + } + } + + rewritelog((r, 3, NULL, "split uri=%s -> uri=%s, args=%s", olduri, + r->filename, r->args ? r->args : "<none>")); + } +} + +/* + * strip 'http[s]://ourhost/' from URI + */ +static void reduce_uri(request_rec *r) +{ + char *cp; + apr_size_t l; + + cp = (char *)ap_http_scheme(r); + l = strlen(cp); + if ( strlen(r->filename) > l+3 + && ap_cstr_casecmpn(r->filename, cp, l) == 0 + && r->filename[l] == ':' + && r->filename[l+1] == '/' + && r->filename[l+2] == '/' ) { + + unsigned short port; + char *portp, *host, *url, *scratch; + + scratch = apr_pstrdup(r->pool, r->filename); /* our scratchpad */ + + /* cut the hostname and port out of the URI */ + cp = host = scratch + l + 3; /* 3 == strlen("://") */ + while (*cp && *cp != '/' && *cp != ':') { + ++cp; + } + + if (*cp == ':') { /* additional port given */ + *cp++ = '\0'; + portp = cp; + while (*cp && *cp != '/') { + ++cp; + } + *cp = '\0'; + + port = atoi(portp); + url = r->filename + (cp - scratch); + if (!*url) { + url = "/"; + } + } + else if (*cp == '/') { /* default port */ + *cp = '\0'; + + port = ap_default_port(r); + url = r->filename + (cp - scratch); + } + else { + port = ap_default_port(r); + url = "/"; + } + + /* now check whether we could reduce it to a local path... */ + if (ap_matches_request_vhost(r, host, port)) { + rewrite_server_conf *conf = + ap_get_module_config(r->server->module_config, &rewrite_module); + rewritelog((r, 3, NULL, "reduce %s -> %s", r->filename, url)); + r->filename = apr_pstrdup(r->pool, url); + + /* remember that the uri was reduced */ + if(!(conf->options & OPTION_LEGACY_PREFIX_DOCROOT)) { + apr_table_setn(r->notes, "mod_rewrite_uri_reduced", "true"); + } + } + } + + return; +} + +/* + * add 'http[s]://ourhost[:ourport]/' to URI + * if URI is still not fully qualified + */ +static void fully_qualify_uri(request_rec *r) +{ + if (r->method_number == M_CONNECT) { + return; + } + else if (!is_absolute_uri(r->filename, NULL)) { + const char *thisserver; + char *thisport; + int port; + + thisserver = ap_get_server_name_for_url(r); + port = ap_get_server_port(r); + thisport = ap_is_default_port(port, r) + ? "" + : apr_psprintf(r->pool, ":%u", port); + + r->filename = apr_psprintf(r->pool, "%s://%s%s%s%s", + ap_http_scheme(r), thisserver, thisport, + (*r->filename == '/') ? "" : "/", + r->filename); + } + + return; +} + +/* + * stat() only the first segment of a path + */ +static int prefix_stat(const char *path, apr_pool_t *pool) +{ + const char *curpath = path; + const char *root; + const char *slash; + char *statpath; + apr_status_t rv; + + rv = apr_filepath_root(&root, &curpath, APR_FILEPATH_TRUENAME, pool); + + if (rv != APR_SUCCESS) { + return 0; + } + + /* let's recognize slashes only, the mod_rewrite semantics are opaque + * enough. + */ + if ((slash = ap_strchr_c(curpath, '/')) != NULL) { + rv = apr_filepath_merge(&statpath, root, + apr_pstrndup(pool, curpath, + (apr_size_t)(slash - curpath)), + APR_FILEPATH_NOTABOVEROOT | + APR_FILEPATH_NOTRELATIVE, pool); + } + else { + rv = apr_filepath_merge(&statpath, root, curpath, + APR_FILEPATH_NOTABOVEROOT | + APR_FILEPATH_NOTRELATIVE, pool); + } + + if (rv == APR_SUCCESS) { + apr_finfo_t sb; + + if (apr_stat(&sb, statpath, APR_FINFO_MIN, pool) == APR_SUCCESS) { + return 1; + } + } + + return 0; +} + +/* + * substitute the prefix path 'match' in 'input' with 'subst' (RewriteBase) + */ +static char *subst_prefix_path(request_rec *r, char *input, const char *match, + const char *subst) +{ + apr_size_t len = strlen(match); + + if (len && match[len - 1] == '/') { + --len; + } + + if (!strncmp(input, match, len) && input[len++] == '/') { + apr_size_t slen, outlen; + char *output; + + rewritelog((r, 5, NULL, "strip matching prefix: %s -> %s", input, + input+len)); + + slen = strlen(subst); + if (slen && subst[slen - 1] != '/') { + ++slen; + } + + outlen = strlen(input) + slen - len; + output = apr_palloc(r->pool, outlen + 1); /* don't forget the \0 */ + + memcpy(output, subst, slen); + if (slen && !output[slen-1]) { + output[slen-1] = '/'; + } + memcpy(output+slen, input+len, outlen - slen); + output[outlen] = '\0'; + + rewritelog((r, 4, NULL, "add subst prefix: %s -> %s", input+len, + output)); + + return output; + } + + /* prefix didn't match */ + return input; +} + + +/* + * +-------------------------------------------------------+ + * | | + * | caching support + * | | + * +-------------------------------------------------------+ + */ + +static void set_cache_value(const char *name, apr_time_t t, char *key, + char *val) +{ + cachedmap *map; + + if (cachep) { +#if APR_HAS_THREADS + apr_thread_mutex_lock(cachep->lock); +#endif + map = apr_hash_get(cachep->maps, name, APR_HASH_KEY_STRING); + + if (!map) { + apr_pool_t *p; + + if (apr_pool_create(&p, cachep->pool) != APR_SUCCESS) { +#if APR_HAS_THREADS + apr_thread_mutex_unlock(cachep->lock); +#endif + return; + } + apr_pool_tag(p, "rewrite_cachedmap"); + + map = apr_palloc(cachep->pool, sizeof(cachedmap)); + map->pool = p; + map->entries = apr_hash_make(map->pool); + map->mtime = t; + + apr_hash_set(cachep->maps, name, APR_HASH_KEY_STRING, map); + } + else if (map->mtime != t) { + apr_pool_clear(map->pool); + map->entries = apr_hash_make(map->pool); + map->mtime = t; + } + + /* Now we should have a valid map->entries hash, where we + * can store our value. + * + * We need to copy the key and the value into OUR pool, + * so that we don't leave it during the r->pool cleanup. + */ + apr_hash_set(map->entries, + apr_pstrdup(map->pool, key), APR_HASH_KEY_STRING, + apr_pstrdup(map->pool, val)); + +#if APR_HAS_THREADS + apr_thread_mutex_unlock(cachep->lock); +#endif + } + + return; +} + +static char *get_cache_value(const char *name, apr_time_t t, char *key, + apr_pool_t *p) +{ + cachedmap *map; + char *val = NULL; + + if (cachep) { +#if APR_HAS_THREADS + apr_thread_mutex_lock(cachep->lock); +#endif + map = apr_hash_get(cachep->maps, name, APR_HASH_KEY_STRING); + + if (map) { + /* if this map is outdated, forget it. */ + if (map->mtime != t) { + apr_pool_clear(map->pool); + map->entries = apr_hash_make(map->pool); + map->mtime = t; + } + else { + val = apr_hash_get(map->entries, key, APR_HASH_KEY_STRING); + if (val) { + /* copy the cached value into the supplied pool, + * where it belongs (r->pool usually) + */ + val = apr_pstrdup(p, val); + } + } + } + +#if APR_HAS_THREADS + apr_thread_mutex_unlock(cachep->lock); +#endif + } + + return val; +} + +static int init_cache(apr_pool_t *p) +{ + cachep = apr_palloc(p, sizeof(cache)); + if (apr_pool_create(&cachep->pool, p) != APR_SUCCESS) { + cachep = NULL; /* turns off cache */ + return 0; + } + apr_pool_tag(cachep->pool, "rewrite_cachep"); + + cachep->maps = apr_hash_make(cachep->pool); +#if APR_HAS_THREADS + (void)apr_thread_mutex_create(&(cachep->lock), APR_THREAD_MUTEX_DEFAULT, p); +#endif + + return 1; +} + + +/* + * +-------------------------------------------------------+ + * | | + * | Map Functions + * | | + * +-------------------------------------------------------+ + */ + +/* + * General Note: key is already a fresh string, created (expanded) just + * for the purpose to be passed in here. So one can modify key itself. + */ + +static char *rewrite_mapfunc_toupper(request_rec *r, char *key) +{ + ap_str_toupper(key); + + return key; +} + +static char *rewrite_mapfunc_tolower(request_rec *r, char *key) +{ + ap_str_tolower(key); + + return key; +} + +static char *rewrite_mapfunc_escape(request_rec *r, char *key) +{ + return ap_escape_uri(r->pool, key); +} + +static char *rewrite_mapfunc_unescape(request_rec *r, char *key) +{ + ap_unescape_url(key); + + return key; +} + +static char *select_random_value_part(request_rec *r, char *value) +{ + char *p = value; + unsigned n = 1; + + /* count number of distinct values */ + while ((p = ap_strchr(p, '|')) != NULL) { + ++n; + ++p; + } + + if (n > 1) { + n = ap_random_pick(1, n); + + /* extract it from the whole string */ + while (--n && (value = ap_strchr(value, '|')) != NULL) { + ++value; + } + + if (value) { /* should not be NULL, but ... */ + p = ap_strchr(value, '|'); + if (p) { + *p = '\0'; + } + } + } + + return value; +} + +/* child process code */ +static void rewrite_child_errfn(apr_pool_t *p, apr_status_t err, + const char *desc) +{ + ap_log_error(APLOG_MARK, APLOG_ERR, err, NULL, APLOGNO(00653) "%s", desc); +} + +static apr_status_t rewritemap_program_child(apr_pool_t *p, + const char *progname, char **argv, + const char *user, const char *group, + apr_file_t **fpout, + apr_file_t **fpin) +{ + apr_status_t rc; + apr_procattr_t *procattr; + apr_proc_t *procnew; + + if ( APR_SUCCESS == (rc=apr_procattr_create(&procattr, p)) + && APR_SUCCESS == (rc=apr_procattr_io_set(procattr, APR_FULL_BLOCK, + APR_FULL_BLOCK, APR_NO_PIPE)) + && APR_SUCCESS == (rc=apr_procattr_dir_set(procattr, + ap_make_dirstr_parent(p, argv[0]))) + && (!user || APR_SUCCESS == (rc=apr_procattr_user_set(procattr, user, ""))) + && (!group || APR_SUCCESS == (rc=apr_procattr_group_set(procattr, group))) + && APR_SUCCESS == (rc=apr_procattr_cmdtype_set(procattr, APR_PROGRAM)) + && APR_SUCCESS == (rc=apr_procattr_child_errfn_set(procattr, + rewrite_child_errfn)) + && APR_SUCCESS == (rc=apr_procattr_error_check_set(procattr, 1))) { + + procnew = apr_pcalloc(p, sizeof(*procnew)); + rc = apr_proc_create(procnew, argv[0], (const char **)argv, NULL, + procattr, p); + + if (rc == APR_SUCCESS) { + apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT); + + if (fpin) { + (*fpin) = procnew->in; + } + + if (fpout) { + (*fpout) = procnew->out; + } + } + } + + return (rc); +} + +static apr_status_t run_rewritemap_programs(server_rec *s, apr_pool_t *p) +{ + rewrite_server_conf *conf; + apr_hash_index_t *hi; + apr_status_t rc; + + conf = ap_get_module_config(s->module_config, &rewrite_module); + + /* If the engine isn't turned on, + * don't even try to do anything. + */ + if (conf->state == ENGINE_DISABLED) { + return APR_SUCCESS; + } + + for (hi = apr_hash_first(p, conf->rewritemaps); hi; hi = apr_hash_next(hi)){ + apr_file_t *fpin = NULL; + apr_file_t *fpout = NULL; + rewritemap_entry *map; + void *val; + + apr_hash_this(hi, NULL, NULL, &val); + map = val; + + if (map->type != MAPTYPE_PRG) { + continue; + } + if (!(map->argv[0]) || !*(map->argv[0]) || map->fpin || map->fpout) { + continue; + } + + rc = rewritemap_program_child(p, map->argv[0], map->argv, + map->user, map->group, + &fpout, &fpin); + if (rc != APR_SUCCESS || fpin == NULL || fpout == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, rc, s, APLOGNO(00654) + "mod_rewrite: could not start RewriteMap " + "program %s", map->checkfile); + return rc; + } + map->fpin = fpin; + map->fpout = fpout; + } + + return APR_SUCCESS; +} + + +/* + * +-------------------------------------------------------+ + * | | + * | Lookup functions + * | | + * +-------------------------------------------------------+ + */ + +static char *lookup_map_txtfile(request_rec *r, const char *file, char *key) +{ + apr_file_t *fp = NULL; + char line[REWRITE_MAX_TXT_MAP_LINE + 1]; /* +1 for \0 */ + char *value, *keylast; + apr_status_t rv; + + if ((rv = apr_file_open(&fp, file, APR_READ|APR_BUFFERED, APR_OS_DEFAULT, + r->pool)) != APR_SUCCESS) + { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00655) + "mod_rewrite: can't open text RewriteMap file %s", file); + return NULL; + } + + keylast = key + strlen(key); + value = NULL; + while (apr_file_gets(line, sizeof(line), fp) == APR_SUCCESS) { + char *p, *c; + + /* ignore comments and lines starting with whitespaces */ + if (*line == '#' || apr_isspace(*line)) { + continue; + } + + p = line; + c = key; + while (c < keylast && *p == *c && !apr_isspace(*p)) { + ++p; + ++c; + } + + /* key doesn't match - ignore. */ + if (c != keylast || !apr_isspace(*p)) { + continue; + } + + /* jump to the value */ + while (apr_isspace(*p)) { + ++p; + } + + /* no value? ignore */ + if (!*p) { + continue; + } + + /* extract the value and return. */ + c = p; + while (*p && !apr_isspace(*p)) { + ++p; + } + value = apr_pstrmemdup(r->pool, c, p - c); + break; + } + apr_file_close(fp); + + return value; +} + +static char *lookup_map_dbmfile(request_rec *r, const char *file, + const char *dbmtype, char *key) +{ +#if APU_MAJOR_VERSION > 1 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 7) + const apr_dbm_driver_t *driver; + const apu_err_t *err; +#endif + apr_dbm_t *dbmfp = NULL; + apr_datum_t dbmkey; + apr_datum_t dbmval; + char *value; + apr_status_t rv; + +#if APU_MAJOR_VERSION > 1 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 7) + if ((rv = apr_dbm_get_driver(&driver, dbmtype, &err, + r->pool)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(10287) + "mod_rewrite: can't load DBM library '%s': %s", + err->reason, err->msg); + return NULL; + } + if ((rv = apr_dbm_open2(&dbmfp, driver, file, APR_DBM_READONLY, + APR_OS_DEFAULT, r->pool)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00656) + "mod_rewrite: can't open DBM RewriteMap %s", file); + return NULL; + } +#else + if ((rv = apr_dbm_open_ex(&dbmfp, dbmtype, file, APR_DBM_READONLY, + APR_OS_DEFAULT, r->pool)) != APR_SUCCESS) + { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00656) + "mod_rewrite: can't open DBM RewriteMap %s", file); + return NULL; + } +#endif + + dbmkey.dptr = key; + dbmkey.dsize = strlen(key); + + if (apr_dbm_fetch(dbmfp, dbmkey, &dbmval) == APR_SUCCESS && dbmval.dptr) { + value = apr_pstrmemdup(r->pool, dbmval.dptr, dbmval.dsize); + } + else { + value = NULL; + } + + apr_dbm_close(dbmfp); + + return value; +} +static char *lookup_map_dbd(request_rec *r, char *key, const char *label) +{ + apr_status_t rv; + apr_dbd_prepared_t *stmt; + const char *errmsg; + apr_dbd_results_t *res = NULL; + apr_dbd_row_t *row = NULL; + char *ret = NULL; + int n = 0; + ap_dbd_t *db = dbd_acquire(r); + + if (db == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02963) + "rewritemap: No db handle available! " + "Check your database access"); + return NULL; + } + + stmt = apr_hash_get(db->prepared, label, APR_HASH_KEY_STRING); + + rv = apr_dbd_pvselect(db->driver, r->pool, db->handle, &res, + stmt, 0, key, NULL); + if (rv != 0) { + errmsg = apr_dbd_error(db->driver, db->handle, rv); + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00657) + "rewritemap: error %s querying for %s", errmsg, key); + return NULL; + } + while ((rv = apr_dbd_get_row(db->driver, r->pool, res, &row, -1)) == 0) { + ++n; + if (ret == NULL) { + ret = apr_pstrdup(r->pool, + apr_dbd_get_entry(db->driver, row, 0)); + } + else { + /* randomise crudely amongst multiple results */ + if ((double)rand() < (double)RAND_MAX/(double)n) { + ret = apr_pstrdup(r->pool, + apr_dbd_get_entry(db->driver, row, 0)); + } + } + } + if (rv != -1) { + errmsg = apr_dbd_error(db->driver, db->handle, rv); + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00658) + "rewritemap: error %s looking up %s", errmsg, key); + } + switch (n) { + case 0: + return NULL; + case 1: + return ret; + default: + /* what's a fair rewritelog level for this? */ + rewritelog((r, 3, NULL, "Multiple values found for %s", key)); + return ret; + } +} + +static char *lookup_map_program(request_rec *r, apr_file_t *fpin, + apr_file_t *fpout, char *key) +{ + char *buf; + char c; + apr_size_t i, nbytes, combined_len = 0; + apr_status_t rv; + const char *eol = APR_EOL_STR; + apr_size_t eolc = 0; + int found_nl = 0; + result_list *buflist = NULL, *curbuf = NULL; + +#ifndef NO_WRITEV + struct iovec iova[2]; + apr_size_t niov; +#endif + + /* when `RewriteEngine off' was used in the per-server + * context then the rewritemap-programs were not spawned. + * In this case using such a map (usually in per-dir context) + * is useless because it is not available. + * + * newlines in the key leave bytes in the pipe and cause + * bad things to happen (next map lookup will use the chars + * after the \n instead of the new key etc etc - in other words, + * the Rewritemap falls out of sync with the requests). + */ + if (fpin == NULL || fpout == NULL || ap_strchr(key, '\n')) { + return NULL; + } + + /* take the lock */ + if (rewrite_mapr_lock_acquire) { + rv = apr_global_mutex_lock(rewrite_mapr_lock_acquire); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00659) + "apr_global_mutex_lock(rewrite_mapr_lock_acquire) " + "failed"); + return NULL; /* Maybe this should be fatal? */ + } + } + + /* write out the request key */ +#ifdef NO_WRITEV + nbytes = strlen(key); + /* XXX: error handling */ + apr_file_write_full(fpin, key, nbytes, NULL); + nbytes = 1; + apr_file_write_full(fpin, "\n", nbytes, NULL); +#else + iova[0].iov_base = key; + iova[0].iov_len = strlen(key); + iova[1].iov_base = "\n"; + iova[1].iov_len = 1; + + niov = 2; + /* XXX: error handling */ + apr_file_writev_full(fpin, iova, niov, &nbytes); +#endif + + buf = apr_palloc(r->pool, REWRITE_PRG_MAP_BUF + 1); + + /* read in the response value */ + nbytes = 1; + apr_file_read(fpout, &c, &nbytes); + do { + i = 0; + while (nbytes == 1 && (i < REWRITE_PRG_MAP_BUF)) { + if (c == eol[eolc]) { + if (!eol[++eolc]) { + /* remove eol from the buffer */ + --eolc; + if (i < eolc) { + curbuf->len -= eolc-i; + i = 0; + } + else { + i -= eolc; + } + ++found_nl; + break; + } + } + + /* only partial (invalid) eol sequence -> reset the counter */ + else if (eolc) { + eolc = 0; + } + + /* catch binary mode, e.g. on Win32 */ + else if (c == '\n') { + ++found_nl; + break; + } + + buf[i++] = c; + apr_file_read(fpout, &c, &nbytes); + } + + /* well, if there wasn't a newline yet, we need to read further */ + if (buflist || (nbytes == 1 && !found_nl)) { + if (!buflist) { + curbuf = buflist = apr_palloc(r->pool, sizeof(*buflist)); + } + else if (i) { + curbuf->next = apr_palloc(r->pool, sizeof(*buflist)); + curbuf = curbuf->next; + + } + curbuf->next = NULL; + + if (i) { + curbuf->string = buf; + curbuf->len = i; + combined_len += i; + buf = apr_palloc(r->pool, REWRITE_PRG_MAP_BUF); + } + + if (nbytes == 1 && !found_nl) { + continue; + } + } + + break; + } while (1); + + /* concat the stuff */ + if (buflist) { + char *p; + + p = buf = apr_palloc(r->pool, combined_len + 1); /* \0 */ + while (buflist) { + if (buflist->len) { + memcpy(p, buflist->string, buflist->len); + p += buflist->len; + } + buflist = buflist->next; + } + *p = '\0'; + i = combined_len; + } + else { + buf[i] = '\0'; + } + + /* give the lock back */ + if (rewrite_mapr_lock_acquire) { + rv = apr_global_mutex_unlock(rewrite_mapr_lock_acquire); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00660) + "apr_global_mutex_unlock(rewrite_mapr_lock_acquire) " + "failed"); + return NULL; /* Maybe this should be fatal? */ + } + } + + /* catch the "failed" case */ + if (i == 4 && !strcasecmp(buf, "NULL")) { + return NULL; + } + + return buf; +} + +/* + * generic map lookup + */ +static char *lookup_map(request_rec *r, char *name, char *key) +{ + rewrite_server_conf *conf; + rewritemap_entry *s; + char *value; + apr_finfo_t st; + apr_status_t rv; + + /* get map configuration */ + conf = ap_get_module_config(r->server->module_config, &rewrite_module); + s = apr_hash_get(conf->rewritemaps, name, APR_HASH_KEY_STRING); + + /* map doesn't exist */ + if (!s) { + return NULL; + } + + switch (s->type) { + /* + * Text file map (perhaps random) + */ + case MAPTYPE_RND: + case MAPTYPE_TXT: + rv = apr_stat(&st, s->checkfile, APR_FINFO_MIN, r->pool); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00661) + "mod_rewrite: can't access text RewriteMap file %s", + s->checkfile); + return NULL; + } + + value = get_cache_value(s->cachename, st.mtime, key, r->pool); + if (!value) { + rewritelog((r, 6, NULL, + "cache lookup FAILED, forcing new map lookup")); + + value = lookup_map_txtfile(r, s->datafile, key); + if (!value) { + rewritelog((r, 5, NULL, "map lookup FAILED: map=%s[txt] key=%s", + name, key)); + set_cache_value(s->cachename, st.mtime, key, ""); + return NULL; + } + + rewritelog((r, 5, NULL,"map lookup OK: map=%s[txt] key=%s -> val=%s", + name, key, value)); + set_cache_value(s->cachename, st.mtime, key, value); + } + else { + rewritelog((r,5,NULL,"cache lookup OK: map=%s[txt] key=%s -> val=%s", + name, key, value)); + } + + if (s->type == MAPTYPE_RND && *value) { + value = select_random_value_part(r, value); + rewritelog((r, 5, NULL, "randomly chosen the subvalue `%s'",value)); + } + + return *value ? value : NULL; + + /* + * DBM file map + */ + case MAPTYPE_DBM: + rv = apr_stat(&st, s->checkfile, APR_FINFO_MIN, r->pool); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00662) + "mod_rewrite: can't access DBM RewriteMap file %s", + s->checkfile); + } + else if(s->checkfile2 != NULL) { + apr_finfo_t st2; + + rv = apr_stat(&st2, s->checkfile2, APR_FINFO_MIN, r->pool); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00663) + "mod_rewrite: can't access DBM RewriteMap " + "file %s", s->checkfile2); + } + else if(st2.mtime > st.mtime) { + st.mtime = st2.mtime; + } + } + if(rv != APR_SUCCESS) { + return NULL; + } + + value = get_cache_value(s->cachename, st.mtime, key, r->pool); + if (!value) { + rewritelog((r, 6, NULL, + "cache lookup FAILED, forcing new map lookup")); + + value = lookup_map_dbmfile(r, s->datafile, s->dbmtype, key); + if (!value) { + rewritelog((r, 5, NULL, "map lookup FAILED: map=%s[dbm] key=%s", + name, key)); + set_cache_value(s->cachename, st.mtime, key, ""); + return NULL; + } + + rewritelog((r, 5, NULL, "map lookup OK: map=%s[dbm] key=%s -> " + "val=%s", name, key, value)); + + set_cache_value(s->cachename, st.mtime, key, value); + return value; + } + + rewritelog((r, 5, NULL, "cache lookup OK: map=%s[dbm] key=%s -> val=%s", + name, key, value)); + return *value ? value : NULL; + + /* + * SQL map without cache + */ + case MAPTYPE_DBD: + value = lookup_map_dbd(r, key, s->dbdq); + if (!value) { + rewritelog((r, 5, NULL, "SQL map lookup FAILED: map %s key=%s", + name, key)); + return NULL; + } + + rewritelog((r, 5, NULL, "SQL map lookup OK: map %s key=%s, val=%s", + name, key, value)); + + return value; + + /* + * SQL map with cache + */ + case MAPTYPE_DBD_CACHE: + value = get_cache_value(s->cachename, 0, key, r->pool); + if (!value) { + rewritelog((r, 6, NULL, + "cache lookup FAILED, forcing new map lookup")); + + value = lookup_map_dbd(r, key, s->dbdq); + if (!value) { + rewritelog((r, 5, NULL, "SQL map lookup FAILED: map %s key=%s", + name, key)); + set_cache_value(s->cachename, 0, key, ""); + return NULL; + } + + rewritelog((r, 5, NULL, "SQL map lookup OK: map %s key=%s, val=%s", + name, key, value)); + + set_cache_value(s->cachename, 0, key, value); + return value; + } + + rewritelog((r, 5, NULL, "cache lookup OK: map=%s[SQL] key=%s, val=%s", + name, key, value)); + return *value ? value : NULL; + + /* + * Program file map + */ + case MAPTYPE_PRG: + value = lookup_map_program(r, s->fpin, s->fpout, key); + if (!value) { + rewritelog((r, 5,NULL,"map lookup FAILED: map=%s key=%s", name, + key)); + return NULL; + } + + rewritelog((r, 5, NULL, "map lookup OK: map=%s key=%s -> val=%s", + name, key, value)); + return value; + + /* + * Internal Map + */ + case MAPTYPE_INT: + value = s->func(r, key); + if (!value) { + rewritelog((r, 5,NULL,"map lookup FAILED: map=%s key=%s", name, + key)); + return NULL; + } + + rewritelog((r, 5, NULL, "map lookup OK: map=%s key=%s -> val=%s", + name, key, value)); + return value; + } + + return NULL; +} + +/* + * lookup a HTTP header and set VARY note + */ +static const char *lookup_header(const char *name, rewrite_ctx *ctx) +{ + const char *val = apr_table_get(ctx->r->headers_in, name); + + /* Skip the 'Vary: Host' header combination + * as indicated in rfc7231 section-7.1.4 + */ + if (val && strcasecmp(name, "Host") != 0) { + ctx->vary_this = ctx->vary_this + ? apr_pstrcat(ctx->r->pool, ctx->vary_this, ", ", + name, NULL) + : apr_pstrdup(ctx->r->pool, name); + } + + return val; +} + +/* + * lookahead helper function + * Determine the correct URI path in perdir context + */ +static APR_INLINE const char *la_u(rewrite_ctx *ctx) +{ + rewrite_perdir_conf *conf; + + if (*ctx->uri == '/') { + return ctx->uri; + } + + conf = ap_get_module_config(ctx->r->per_dir_config, &rewrite_module); + + return apr_pstrcat(ctx->r->pool, conf->baseurl + ? conf->baseurl : conf->directory, + ctx->uri, NULL); +} + +/* + * generic variable lookup + */ +static char *lookup_variable(char *var, rewrite_ctx *ctx) +{ + const char *result; + request_rec *r = ctx->r; + apr_size_t varlen = strlen(var); + + /* fast exit */ + if (varlen < 4) { + return ""; + } + + result = NULL; + + /* fast tests for variable length variables (sic) first */ + if (var[3] == ':') { + if (var[4] && !strncasecmp(var, "ENV", 3)) { + var += 4; + result = apr_table_get(r->notes, var); + + if (!result) { + result = apr_table_get(r->subprocess_env, var); + } + if (!result) { + result = getenv(var); + } + } + else if (var[4] && !strncasecmp(var, "SSL", 3)) { + result = ap_ssl_var_lookup(r->pool, r->server, r->connection, r, + var + 4); + } + } + else if (var[4] == ':') { + if (var[5]) { + request_rec *rr; + const char *path; + + if (!strncasecmp(var, "HTTP", 4)) { + result = lookup_header(var+5, ctx); + } + else if (!strncasecmp(var, "LA-U", 4)) { + if (ctx->uri && subreq_ok(r)) { + path = ctx->perdir ? la_u(ctx) : ctx->uri; + rr = ap_sub_req_lookup_uri(path, r, NULL); + ctx->r = rr; + result = apr_pstrdup(r->pool, lookup_variable(var+5, ctx)); + ctx->r = r; + ap_destroy_sub_req(rr); + + rewritelog((r, 5, ctx->perdir, "lookahead: path=%s var=%s " + "-> val=%s", path, var+5, result)); + + return (char *)result; + } + } + else if (!strncasecmp(var, "LA-F", 4)) { + if (ctx->uri && subreq_ok(r)) { + path = ctx->uri; + if (ctx->perdir && *path == '/') { + /* sigh, the user wants a file based subrequest, but + * we can't do one, since we don't know what the file + * path is! In this case behave like LA-U. + */ + rr = ap_sub_req_lookup_uri(path, r, NULL); + } + else { + if (ctx->perdir) { + rewrite_perdir_conf *conf; + + conf = ap_get_module_config(r->per_dir_config, + &rewrite_module); + + path = apr_pstrcat(r->pool, conf->directory, path, + NULL); + } + + rr = ap_sub_req_lookup_file(path, r, NULL); + } + + ctx->r = rr; + result = apr_pstrdup(r->pool, lookup_variable(var+5, ctx)); + ctx->r = r; + ap_destroy_sub_req(rr); + + rewritelog((r, 5, ctx->perdir, "lookahead: path=%s var=%s " + "-> val=%s", path, var+5, result)); + + return (char *)result; + } + } + } + } + + /* well, do it the hard way */ + else { + apr_time_exp_t tm; + + /* can't do this above, because of the getenv call */ + ap_str_toupper(var); + + switch (varlen) { + case 4: + if (!strcmp(var, "TIME")) { + apr_time_exp_lt(&tm, apr_time_now()); + result = apr_psprintf(r->pool, "%04d%02d%02d%02d%02d%02d", + tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + rewritelog((r, 1, ctx->perdir, "RESULT='%s'", result)); + return (char *)result; + } + else if (!strcmp(var, "IPV6")) { + int flag = FALSE; +#if APR_HAVE_IPV6 + apr_sockaddr_t *addr = r->useragent_addr; + flag = (addr->family == AF_INET6 && + !IN6_IS_ADDR_V4MAPPED((struct in6_addr *)addr->ipaddr_ptr)); + rewritelog((r, 1, ctx->perdir, "IPV6='%s'", flag ? "on" : "off")); +#else + rewritelog((r, 1, ctx->perdir, "IPV6='off' (IPv6 is not enabled)")); +#endif + result = (flag ? "on" : "off"); + } + break; + + case 5: + if (!strcmp(var, "HTTPS")) { + int flag = ap_ssl_conn_is_ssl(r->connection); + return apr_pstrdup(r->pool, flag ? "on" : "off"); + } + break; + + case 8: + switch (var[6]) { + case 'A': + if (!strcmp(var, "TIME_DAY")) { + apr_time_exp_lt(&tm, apr_time_now()); + return apr_psprintf(r->pool, "%02d", tm.tm_mday); + } + break; + + case 'E': + if (!strcmp(var, "TIME_SEC")) { + apr_time_exp_lt(&tm, apr_time_now()); + return apr_psprintf(r->pool, "%02d", tm.tm_sec); + } + break; + + case 'I': + if (!strcmp(var, "TIME_MIN")) { + apr_time_exp_lt(&tm, apr_time_now()); + return apr_psprintf(r->pool, "%02d", tm.tm_min); + } + break; + + case 'O': + if (!strcmp(var, "TIME_MON")) { + apr_time_exp_lt(&tm, apr_time_now()); + return apr_psprintf(r->pool, "%02d", tm.tm_mon+1); + } + break; + } + break; + + case 9: + switch (var[7]) { + case 'A': + if (var[8] == 'Y' && !strcmp(var, "TIME_WDAY")) { + apr_time_exp_lt(&tm, apr_time_now()); + return apr_psprintf(r->pool, "%d", tm.tm_wday); + } + else if (!strcmp(var, "TIME_YEAR")) { + apr_time_exp_lt(&tm, apr_time_now()); + return apr_psprintf(r->pool, "%04d", tm.tm_year+1900); + } + break; + + case 'E': + if (!strcmp(var, "IS_SUBREQ")) { + result = (r->main ? "true" : "false"); + } + break; + + case 'F': + if (!strcmp(var, "PATH_INFO")) { + result = r->path_info; + } + break; + + case 'P': + if (!strcmp(var, "AUTH_TYPE")) { + result = r->ap_auth_type; + } + break; + + case 'S': + if (!strcmp(var, "HTTP_HOST")) { + result = lookup_header("Host", ctx); + } + break; + + case 'U': + if (!strcmp(var, "TIME_HOUR")) { + apr_time_exp_lt(&tm, apr_time_now()); + return apr_psprintf(r->pool, "%02d", tm.tm_hour); + } + break; + } + break; + + case 11: + switch (var[8]) { + case 'A': + if (!strcmp(var, "SERVER_NAME")) { + result = ap_get_server_name_for_url(r); + } + break; + + case 'D': + if (*var == 'R' && !strcmp(var, "REMOTE_ADDR")) { + result = r->useragent_ip; + } + else if (!strcmp(var, "SERVER_ADDR")) { + result = r->connection->local_ip; + } + break; + + case 'E': + if (*var == 'H' && !strcmp(var, "HTTP_ACCEPT")) { + result = lookup_header("Accept", ctx); + } + else if (!strcmp(var, "THE_REQUEST")) { + result = r->the_request; + } + break; + + case 'I': + if (!strcmp(var, "API_VERSION")) { + return apr_psprintf(r->pool, "%d:%d", + MODULE_MAGIC_NUMBER_MAJOR, + MODULE_MAGIC_NUMBER_MINOR); + } + break; + + case 'K': + if (!strcmp(var, "HTTP_COOKIE")) { + result = lookup_header("Cookie", ctx); + } + break; + + case 'O': + if (*var == 'S' && !strcmp(var, "SERVER_PORT")) { + return apr_psprintf(r->pool, "%u", ap_get_server_port(r)); + } + else if (var[7] == 'H' && !strcmp(var, "REMOTE_HOST")) { + result = ap_get_remote_host(r->connection,r->per_dir_config, + REMOTE_NAME, NULL); + } + else if (!strcmp(var, "REMOTE_PORT")) { + return apr_itoa(r->pool, r->useragent_addr->port); + } + break; + + case 'S': + if (*var == 'R' && !strcmp(var, "REMOTE_USER")) { + result = r->user; + } + else if (!strcmp(var, "SCRIPT_USER")) { + result = "<unknown>"; + if (r->finfo.valid & APR_FINFO_USER) { + apr_uid_name_get((char **)&result, r->finfo.user, + r->pool); + } + } + break; + + case 'U': + if (!strcmp(var, "REQUEST_URI")) { + result = r->uri; + } + break; + } + break; + + case 12: + switch (var[3]) { + case 'I': + if (!strcmp(var, "SCRIPT_GROUP")) { + result = "<unknown>"; + if (r->finfo.valid & APR_FINFO_GROUP) { + apr_gid_name_get((char **)&result, r->finfo.group, + r->pool); + } + } + break; + + case 'O': + if (!strcmp(var, "REMOTE_IDENT")) { + result = ap_get_remote_logname(r); + } + break; + + case 'P': + if (!strcmp(var, "HTTP_REFERER")) { + result = lookup_header("Referer", ctx); + } + break; + + case 'R': + if (!strcmp(var, "QUERY_STRING")) { + result = r->args; + } + break; + + case 'V': + if (!strcmp(var, "SERVER_ADMIN")) { + result = r->server->server_admin; + } + break; + } + break; + + case 13: + if (!strcmp(var, "DOCUMENT_ROOT")) { + result = ap_document_root(r); + } + break; + + case 14: + if (*var == 'H' && !strcmp(var, "HTTP_FORWARDED")) { + result = lookup_header("Forwarded", ctx); + } + else if (*var == 'C' && !strcmp(var, "CONTEXT_PREFIX")) { + result = ap_context_prefix(r); + } + else if (var[8] == 'M' && !strcmp(var, "REQUEST_METHOD")) { + result = r->method; + } + else if (!strcmp(var, "REQUEST_SCHEME")) { + result = ap_http_scheme(r); + } + break; + + case 15: + switch (var[7]) { + case 'E': + if (!strcmp(var, "HTTP_USER_AGENT")) { + result = lookup_header("User-Agent", ctx); + } + break; + + case 'F': + if (!strcmp(var, "SCRIPT_FILENAME")) { + result = r->filename; /* same as request_filename (16) */ + } + break; + + case 'P': + if (!strcmp(var, "SERVER_PROTOCOL")) { + result = r->protocol; + } + break; + + case 'S': + if (!strcmp(var, "SERVER_SOFTWARE")) { + result = ap_get_server_banner(); + } + break; + } + break; + + case 16: + if (*var == 'C' && !strcmp(var, "CONN_REMOTE_ADDR")) { + result = r->connection->client_ip; + } + else if (!strcmp(var, "REQUEST_FILENAME")) { + result = r->filename; /* same as script_filename (15) */ + } + break; + + case 21: + if (!strcmp(var, "HTTP_PROXY_CONNECTION")) { + result = lookup_header("Proxy-Connection", ctx); + } + else if (!strcmp(var, "CONTEXT_DOCUMENT_ROOT")) { + result = ap_context_document_root(r); + } + break; + } + } + + return apr_pstrdup(r->pool, result ? result : ""); +} + + +/* + * +-------------------------------------------------------+ + * | | + * | Expansion functions + * | | + * +-------------------------------------------------------+ + */ + +/* + * Bracketed expression handling + * s points after the opening bracket + */ +static APR_INLINE char *find_closing_curly(char *s) +{ + unsigned depth; + + for (depth = 1; *s; ++s) { + if (*s == RIGHT_CURLY && --depth == 0) { + return s; + } + else if (*s == LEFT_CURLY) { + ++depth; + } + } + + return NULL; +} + +static APR_INLINE char *find_char_in_curlies(char *s, int c) +{ + unsigned depth; + + for (depth = 1; *s; ++s) { + if (*s == c && depth == 1) { + return s; + } + else if (*s == RIGHT_CURLY && --depth == 0) { + return NULL; + } + else if (*s == LEFT_CURLY) { + ++depth; + } + } + + return NULL; +} + +/* perform all the expansions on the input string + * putting the result into a new string + * + * for security reasons this expansion must be performed in a + * single pass, otherwise an attacker can arrange for the result + * of an earlier expansion to include expansion specifiers that + * are interpreted by a later expansion, producing results that + * were not intended by the administrator. + */ +static char *do_expand(char *input, rewrite_ctx *ctx, rewriterule_entry *entry) +{ + result_list *result, *current; + result_list sresult[SMALL_EXPANSION]; + unsigned spc = 0; + apr_size_t span, inputlen, outlen; + char *p, *c; + apr_pool_t *pool = ctx->r->pool; + + span = strcspn(input, "\\$%"); + inputlen = strlen(input); + + /* fast exit */ + if (inputlen == span) { + return apr_pstrmemdup(pool, input, inputlen); + } + + /* well, actually something to do */ + result = current = &(sresult[spc++]); + + p = input + span; + current->next = NULL; + current->string = input; + current->len = span; + outlen = span; + + /* loop for specials */ + do { + /* prepare next entry */ + if (current->len) { + current->next = (spc < SMALL_EXPANSION) + ? &(sresult[spc++]) + : (result_list *)apr_palloc(pool, + sizeof(result_list)); + current = current->next; + current->next = NULL; + current->len = 0; + } + + /* escaped character */ + if (*p == '\\') { + current->len = 1; + ++outlen; + if (!p[1]) { + current->string = p; + break; + } + else { + current->string = ++p; + ++p; + } + } + + /* variable or map lookup */ + else if (p[1] == '{') { + char *endp; + + endp = find_closing_curly(p+2); + if (!endp) { + current->len = 2; + current->string = p; + outlen += 2; + p += 2; + } + + /* variable lookup */ + else if (*p == '%') { + p = lookup_variable(apr_pstrmemdup(pool, p+2, endp-p-2), ctx); + + span = strlen(p); + current->len = span; + current->string = p; + outlen += span; + p = endp + 1; + } + + /* map lookup */ + else { /* *p == '$' */ + char *key; + + /* + * To make rewrite maps useful, the lookup key and + * default values must be expanded, so we make + * recursive calls to do the work. For security + * reasons we must never expand a string that includes + * verbatim data from the network. The recursion here + * isn't a problem because the result of expansion is + * only passed to lookup_map() so it cannot be + * re-expanded, only re-looked-up. Another way of + * looking at it is that the recursion is entirely + * driven by the syntax of the nested curly brackets. + */ + + key = find_char_in_curlies(p+2, ':'); + if (!key) { + current->len = 2; + current->string = p; + outlen += 2; + p += 2; + } + else { + char *map, *dflt; + + map = apr_pstrmemdup(pool, p+2, endp-p-2); + key = map + (key-p-2); + *key++ = '\0'; + dflt = find_char_in_curlies(key, '|'); + if (dflt) { + *dflt++ = '\0'; + } + + /* reuse of key variable as result */ + key = lookup_map(ctx->r, map, do_expand(key, ctx, entry)); + + if (!key && dflt && *dflt) { + key = do_expand(dflt, ctx, entry); + } + + if (key) { + span = strlen(key); + current->len = span; + current->string = key; + outlen += span; + } + + p = endp + 1; + } + } + } + + /* backreference */ + else if (apr_isdigit(p[1])) { + int n = p[1] - '0'; + backrefinfo *bri = (*p == '$') ? &ctx->briRR : &ctx->briRC; + + /* see ap_pregsub() in server/util.c */ + if (bri->source && n < AP_MAX_REG_MATCH + && bri->regmatch[n].rm_eo > bri->regmatch[n].rm_so) { + span = bri->regmatch[n].rm_eo - bri->regmatch[n].rm_so; + if (entry && (entry->flags & RULEFLAG_ESCAPEBACKREF)) { + /* escape the backreference */ + char *tmp2, *tmp; + tmp = apr_pstrmemdup(pool, bri->source + bri->regmatch[n].rm_so, span); + tmp2 = escape_backref(pool, tmp, entry->escapes, entry->noescapes, + entry->flags); + rewritelog((ctx->r, 5, ctx->perdir, "escaping backreference '%s' to '%s'", + tmp, tmp2)); + + current->len = span = strlen(tmp2); + current->string = tmp2; + } else { + current->len = span; + current->string = bri->source + bri->regmatch[n].rm_so; + } + + outlen += span; + } + + p += 2; + } + + /* not for us, just copy it */ + else { + current->len = 1; + current->string = p++; + ++outlen; + } + + /* check the remainder */ + if (*p && (span = strcspn(p, "\\$%")) > 0) { + if (current->len) { + current->next = (spc < SMALL_EXPANSION) + ? &(sresult[spc++]) + : (result_list *)apr_palloc(pool, + sizeof(result_list)); + current = current->next; + current->next = NULL; + } + + current->len = span; + current->string = p; + p += span; + outlen += span; + } + + } while (p < input+inputlen); + + /* assemble result */ + c = p = apr_palloc(pool, outlen + 1); /* don't forget the \0 */ + do { + if (result->len) { + ap_assert(c+result->len <= p+outlen); /* XXX: can be removed after + * extensive testing and + * review + */ + memcpy(c, result->string, result->len); + c += result->len; + } + result = result->next; + } while (result); + + p[outlen] = '\0'; + + return p; +} + +/* + * perform all the expansions on the environment variables + */ +static void do_expand_env(data_item *env, rewrite_ctx *ctx) +{ + char *name, *val; + + while (env) { + name = do_expand(env->data, ctx, NULL); + if (*name == '!') { + name++; + apr_table_unset(ctx->r->subprocess_env, name); + rewritelog((ctx->r, 5, NULL, "unsetting env variable '%s'", name)); + } + else { + if ((val = ap_strchr(name, ':')) != NULL) { + *val++ = '\0'; + } else { + val = ""; + } + + apr_table_set(ctx->r->subprocess_env, name, val); + rewritelog((ctx->r, 5, NULL, "setting env variable '%s' to '%s'", + name, val)); + } + + env = env->next; + } + + return; +} + +/* + * perform all the expansions on the cookies + * + * TODO: use cached time similar to how logging does it + */ +static void add_cookie(request_rec *r, char *s) +{ + char *var; + char *val; + char *domain; + char *expires; + char *path; + char *secure; + char *httponly; + char *samesite; + + char *tok_cntx; + char *cookie; + /* long-standing default, but can't use ':' in a cookie */ + const char *sep = ":"; + + /* opt-in to ; separator if first character is a ; */ + if (s && *s == ';') { + sep = ";"; + s++; + } + + var = apr_strtok(s, sep, &tok_cntx); + val = apr_strtok(NULL, sep, &tok_cntx); + domain = apr_strtok(NULL, sep, &tok_cntx); + + if (var && val && domain) { + request_rec *rmain = r; + char *notename; + void *data; + + while (rmain->main) { + rmain = rmain->main; + } + + notename = apr_pstrcat(rmain->pool, var, "_rewrite", NULL); + apr_pool_userdata_get(&data, notename, rmain->pool); + if (!data) { + char *exp_time = NULL; + + expires = apr_strtok(NULL, sep, &tok_cntx); + path = expires ? apr_strtok(NULL, sep, &tok_cntx) : NULL; + secure = path ? apr_strtok(NULL, sep, &tok_cntx) : NULL; + httponly = secure ? apr_strtok(NULL, sep, &tok_cntx) : NULL; + samesite = httponly ? apr_strtok(NULL, sep, &tok_cntx) : NULL; + + if (expires) { + apr_time_exp_t tms; + long exp_min; + + exp_min = atol(expires); + if (exp_min) { + apr_time_exp_gmt(&tms, r->request_time + + apr_time_from_sec((60 * exp_min))); + exp_time = apr_psprintf(r->pool, "%s, %.2d-%s-%.4d " + "%.2d:%.2d:%.2d GMT", + apr_day_snames[tms.tm_wday], + tms.tm_mday, + apr_month_snames[tms.tm_mon], + tms.tm_year+1900, + tms.tm_hour, tms.tm_min, tms.tm_sec); + } + } + + cookie = apr_pstrcat(rmain->pool, + var, "=", val, + "; path=", path ? path : "/", + "; domain=", domain, + expires ? (exp_time ? "; expires=" : "") + : NULL, + expires ? (exp_time ? exp_time : "") + : NULL, + (secure && (!ap_cstr_casecmp(secure, "true") + || !strcmp(secure, "1") + || !ap_cstr_casecmp(secure, + "secure"))) ? + "; secure" : NULL, + (httponly && (!ap_cstr_casecmp(httponly, "true") + || !strcmp(httponly, "1") + || !ap_cstr_casecmp(httponly, + "HttpOnly"))) ? + "; HttpOnly" : NULL, + NULL); + + if (samesite && strcmp(samesite, "0") && ap_cstr_casecmp(samesite,"false")) { + cookie = apr_pstrcat(rmain->pool, cookie, "; SameSite=", + samesite, NULL); + } + + apr_table_addn(rmain->err_headers_out, "Set-Cookie", cookie); + apr_pool_userdata_set("set", notename, NULL, rmain->pool); + rewritelog((rmain, 5, NULL, "setting cookie '%s'", cookie)); + } + else { + rewritelog((rmain, 5, NULL, "skipping already set cookie '%s'", + var)); + } + } + + return; +} + +static void do_expand_cookie(data_item *cookie, rewrite_ctx *ctx) +{ + while (cookie) { + add_cookie(ctx->r, do_expand(cookie->data, ctx, NULL)); + cookie = cookie->next; + } + + return; +} + +#if APR_HAS_USER +/* + * Expand tilde-paths (/~user) through Unix /etc/passwd + * database information (or other OS-specific database) + */ +static char *expand_tildepaths(request_rec *r, char *uri) +{ + if (uri && *uri == '/' && uri[1] == '~') { + char *p, *user; + + p = user = uri + 2; + while (*p && *p != '/') { + ++p; + } + + if (p > user) { + char *homedir; + + user = apr_pstrmemdup(r->pool, user, p-user); + if (apr_uid_homepath_get(&homedir, user, r->pool) == APR_SUCCESS) { + if (*p) { + /* reuse of user variable */ + user = homedir + strlen(homedir) - 1; + if (user >= homedir && *user == '/') { + *user = '\0'; + } + + return apr_pstrcat(r->pool, homedir, p, NULL); + } + else { + return homedir; + } + } + } + } + + return uri; +} +#endif /* if APR_HAS_USER */ + + +/* + * +-------------------------------------------------------+ + * | | + * | rewriting lockfile support + * | | + * +-------------------------------------------------------+ + */ + +static apr_status_t rewritelock_create(server_rec *s, apr_pool_t *p) +{ + apr_status_t rc; + + /* create the lockfile */ + rc = ap_global_mutex_create(&rewrite_mapr_lock_acquire, NULL, + rewritemap_mutex_type, NULL, s, p, 0); + if (rc != APR_SUCCESS) { + return rc; + } + + return APR_SUCCESS; +} + +static apr_status_t rewritelock_remove(void *data) +{ + /* destroy the rewritelock */ + if (rewrite_mapr_lock_acquire) { + apr_global_mutex_destroy(rewrite_mapr_lock_acquire); + rewrite_mapr_lock_acquire = NULL; + } + return APR_SUCCESS; +} + + +/* + * +-------------------------------------------------------+ + * | | + * | configuration directive handling + * | | + * +-------------------------------------------------------+ + */ + +/* + * own command line parser for RewriteRule and RewriteCond, + * which doesn't have the '\\' problem. + * (returns true on error) + * + * XXX: what an inclined parser. Seems we have to leave it so + * for backwards compat. *sigh* + */ +static int parseargline(char *str, char **a1, char **a2, char **a2_end, char **a3) +{ + char quote; + + while (apr_isspace(*str)) { + ++str; + } + + /* + * determine first argument + */ + quote = (*str == '"' || *str == '\'') ? *str++ : '\0'; + *a1 = str; + + for (; *str; ++str) { + if ((apr_isspace(*str) && !quote) || (*str == quote)) { + break; + } + if (*str == '\\' && apr_isspace(str[1])) { + ++str; + continue; + } + } + + if (!*str) { + return 1; + } + *str++ = '\0'; + + while (apr_isspace(*str)) { + ++str; + } + + /* + * determine second argument + */ + quote = (*str == '"' || *str == '\'') ? *str++ : '\0'; + *a2 = str; + + for (; *str; ++str) { + if ((apr_isspace(*str) && !quote) || (*str == quote)) { + break; + } + if (*str == '\\' && apr_isspace(str[1])) { + ++str; + continue; + } + } + + if (!*str) { + *a3 = NULL; /* 3rd argument is optional */ + *a2_end = str; + return 0; + } + *a2_end = str; + *str++ = '\0'; + + while (apr_isspace(*str)) { + ++str; + } + + if (!*str) { + *a3 = NULL; /* 3rd argument is still optional */ + return 0; + } + + /* + * determine third argument + */ + quote = (*str == '"' || *str == '\'') ? *str++ : '\0'; + *a3 = str; + for (; *str; ++str) { + if ((apr_isspace(*str) && !quote) || (*str == quote)) { + break; + } + if (*str == '\\' && apr_isspace(str[1])) { + ++str; + continue; + } + } + *str = '\0'; + + return 0; +} + +static void *config_server_create(apr_pool_t *p, server_rec *s) +{ + rewrite_server_conf *a; + + a = (rewrite_server_conf *)apr_pcalloc(p, sizeof(rewrite_server_conf)); + + a->state = ENGINE_DISABLED; + a->options = OPTION_NONE; + a->rewritemaps = apr_hash_make(p); + a->rewriteconds = apr_array_make(p, 2, sizeof(rewritecond_entry)); + a->rewriterules = apr_array_make(p, 2, sizeof(rewriterule_entry)); + a->server = s; + + return (void *)a; +} + +static void *config_server_merge(apr_pool_t *p, void *basev, void *overridesv) +{ + rewrite_server_conf *a, *base, *overrides; + + a = (rewrite_server_conf *)apr_pcalloc(p, + sizeof(rewrite_server_conf)); + base = (rewrite_server_conf *)basev; + overrides = (rewrite_server_conf *)overridesv; + + a->state = (overrides->state_set == 0) ? base->state : overrides->state; + a->state_set = overrides->state_set || base->state_set; + a->options = (overrides->options_set == 0) ? base->options : overrides->options; + a->options_set = overrides->options_set || base->options_set; + + a->server = overrides->server; + + if (a->options & OPTION_INHERIT || + (base->options & OPTION_INHERIT_DOWN && + !(a->options & OPTION_IGNORE_INHERIT))) { + /* + * local directives override + * and anything else is inherited + */ + a->rewritemaps = apr_hash_overlay(p, overrides->rewritemaps, + base->rewritemaps); + a->rewriteconds = apr_array_append(p, overrides->rewriteconds, + base->rewriteconds); + a->rewriterules = apr_array_append(p, overrides->rewriterules, + base->rewriterules); + } + else if (a->options & OPTION_INHERIT_BEFORE || + (base->options & OPTION_INHERIT_DOWN_BEFORE && + !(a->options & OPTION_IGNORE_INHERIT))) { + /* + * local directives override + * and anything else is inherited (preserving order) + */ + a->rewritemaps = apr_hash_overlay(p, base->rewritemaps, + overrides->rewritemaps); + a->rewriteconds = apr_array_append(p, base->rewriteconds, + overrides->rewriteconds); + a->rewriterules = apr_array_append(p, base->rewriterules, + overrides->rewriterules); + } + else { + /* + * local directives override + * and anything else gets defaults + */ + a->rewritemaps = overrides->rewritemaps; + a->rewriteconds = overrides->rewriteconds; + a->rewriterules = overrides->rewriterules; + } + + return (void *)a; +} + +static void *config_perdir_create(apr_pool_t *p, char *path) +{ + rewrite_perdir_conf *a; + + a = (rewrite_perdir_conf *)apr_pcalloc(p, sizeof(rewrite_perdir_conf)); + + a->state = ENGINE_DISABLED; + a->options = OPTION_NONE; + a->baseurl = NULL; + a->rewriteconds = apr_array_make(p, 2, sizeof(rewritecond_entry)); + a->rewriterules = apr_array_make(p, 2, sizeof(rewriterule_entry)); + + if (path == NULL) { + a->directory = NULL; + } + else { + /* make sure it has a trailing slash */ + if (path[strlen(path)-1] == '/') { + a->directory = apr_pstrdup(p, path); + } + else { + a->directory = apr_pstrcat(p, path, "/", NULL); + } + } + + return (void *)a; +} + +static void *config_perdir_merge(apr_pool_t *p, void *basev, void *overridesv) +{ + rewrite_perdir_conf *a, *base, *overrides; + + a = (rewrite_perdir_conf *)apr_pcalloc(p, + sizeof(rewrite_perdir_conf)); + base = (rewrite_perdir_conf *)basev; + overrides = (rewrite_perdir_conf *)overridesv; + + a->state = (overrides->state_set == 0) ? base->state : overrides->state; + a->state_set = overrides->state_set || base->state_set; + a->options = (overrides->options_set == 0) ? base->options : overrides->options; + a->options_set = overrides->options_set || base->options_set; + + if (a->options & OPTION_MERGEBASE) { + a->baseurl = (overrides->baseurl_set == 0) ? base->baseurl : overrides->baseurl; + a->baseurl_set = overrides->baseurl_set || base->baseurl_set; + } + else { + a->baseurl = overrides->baseurl; + } + + a->directory = overrides->directory; + + if (a->options & OPTION_INHERIT || + (base->options & OPTION_INHERIT_DOWN && + !(a->options & OPTION_IGNORE_INHERIT))) { + a->rewriteconds = apr_array_append(p, overrides->rewriteconds, + base->rewriteconds); + a->rewriterules = apr_array_append(p, overrides->rewriterules, + base->rewriterules); + } + else if (a->options & OPTION_INHERIT_BEFORE || + (base->options & OPTION_INHERIT_DOWN_BEFORE && + !(a->options & OPTION_IGNORE_INHERIT))) { + a->rewriteconds = apr_array_append(p, base->rewriteconds, + overrides->rewriteconds); + a->rewriterules = apr_array_append(p, base->rewriterules, + overrides->rewriterules); + } + else { + a->rewriteconds = overrides->rewriteconds; + a->rewriterules = overrides->rewriterules; + } + + return (void *)a; +} + +static const char *cmd_rewriteengine(cmd_parms *cmd, + void *in_dconf, int flag) +{ + rewrite_perdir_conf *dconf = in_dconf; + rewrite_server_conf *sconf; + + sconf = ap_get_module_config(cmd->server->module_config, &rewrite_module); + + /* server command? set both global scope and base directory scope */ + if (cmd->path == NULL) { + sconf->state = (flag ? ENGINE_ENABLED : ENGINE_DISABLED); + sconf->state_set = 1; + dconf->state = sconf->state; + dconf->state_set = 1; + } + /* directory command? set directory scope only */ + else { + dconf->state = (flag ? ENGINE_ENABLED : ENGINE_DISABLED); + dconf->state_set = 1; + } + + return NULL; +} + +static const char *cmd_rewriteoptions(cmd_parms *cmd, + void *in_dconf, const char *option) +{ + int options = 0; + + while (*option) { + char *w = ap_getword_conf(cmd->temp_pool, &option); + + if (!strcasecmp(w, "inherit")) { + options |= OPTION_INHERIT; + } + else if (!strcasecmp(w, "inheritbefore")) { + options |= OPTION_INHERIT_BEFORE; + } + else if (!strcasecmp(w, "inheritdown")) { + options |= OPTION_INHERIT_DOWN; + } + else if(!strcasecmp(w, "inheritdownbefore")) { + options |= OPTION_INHERIT_DOWN_BEFORE; + } + else if (!strcasecmp(w, "ignoreinherit")) { + options |= OPTION_IGNORE_INHERIT; + } + else if (!strcasecmp(w, "allownoslash")) { + options |= OPTION_NOSLASH; + } + else if (!strncasecmp(w, "MaxRedirects=", 13)) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00664) + "RewriteOptions: MaxRedirects option has been " + "removed in favor of the global " + "LimitInternalRecursion directive and will be " + "ignored."); + } + else if (!strcasecmp(w, "allowanyuri")) { + options |= OPTION_ANYURI; + } + else if (!strcasecmp(w, "mergebase")) { + options |= OPTION_MERGEBASE; + } + else if (!strcasecmp(w, "ignorecontextinfo")) { + options |= OPTION_IGNORE_CONTEXT_INFO; + } + else if (!strcasecmp(w, "legacyprefixdocroot")) { + options |= OPTION_LEGACY_PREFIX_DOCROOT; + } + else { + return apr_pstrcat(cmd->pool, "RewriteOptions: unknown option '", + w, "'", NULL); + } + } + + /* server command? set both global scope and base directory scope */ + if (cmd->path == NULL) { /* is server command */ + rewrite_perdir_conf *dconf = in_dconf; + rewrite_server_conf *sconf = + ap_get_module_config(cmd->server->module_config, + &rewrite_module); + + sconf->options |= options; + sconf->options_set = 1; + dconf->options |= options; + dconf->options_set = 1; + } + /* directory command? set directory scope only */ + else { /* is per-directory command */ + rewrite_perdir_conf *dconf = in_dconf; + + dconf->options |= options; + dconf->options_set = 1; + } + + return NULL; +} + +static const char *cmd_rewritemap(cmd_parms *cmd, void *dconf, const char *a1, + const char *a2, const char *a3) +{ + rewrite_server_conf *sconf; + rewritemap_entry *newmap; + apr_finfo_t st; + const char *fname; + + sconf = ap_get_module_config(cmd->server->module_config, &rewrite_module); + + newmap = apr_pcalloc(cmd->pool, sizeof(rewritemap_entry)); + + if (strncasecmp(a2, "txt:", 4) == 0) { + if ((fname = ap_server_root_relative(cmd->pool, a2+4)) == NULL) { + return apr_pstrcat(cmd->pool, "RewriteMap: bad path to txt map: ", + a2+4, NULL); + } + + newmap->type = MAPTYPE_TXT; + newmap->datafile = fname; + newmap->checkfile = fname; + newmap->cachename = apr_psprintf(cmd->pool, "%pp:%s", + (void *)cmd->server, a1); + } + else if (strncasecmp(a2, "rnd:", 4) == 0) { + if ((fname = ap_server_root_relative(cmd->pool, a2+4)) == NULL) { + return apr_pstrcat(cmd->pool, "RewriteMap: bad path to rnd map: ", + a2+4, NULL); + } + + newmap->type = MAPTYPE_RND; + newmap->datafile = fname; + newmap->checkfile = fname; + newmap->cachename = apr_psprintf(cmd->pool, "%pp:%s", + (void *)cmd->server, a1); + } + else if (strncasecmp(a2, "dbm", 3) == 0) { + apr_status_t rv; + + newmap->type = MAPTYPE_DBM; + fname = NULL; + newmap->cachename = apr_psprintf(cmd->pool, "%pp:%s", + (void *)cmd->server, a1); + + if (a2[3] == ':') { + newmap->dbmtype = "default"; + fname = a2+4; + } + else if (a2[3] == '=') { + const char *colon = ap_strchr_c(a2 + 4, ':'); + + if (colon) { + newmap->dbmtype = apr_pstrndup(cmd->pool, a2 + 4, + colon - (a2 + 3) - 1); + fname = colon + 1; + } + } + + if (!fname) { + return apr_pstrcat(cmd->pool, "RewriteMap: bad map:", + a2, NULL); + } + + if ((newmap->datafile = ap_server_root_relative(cmd->pool, + fname)) == NULL) { + return apr_pstrcat(cmd->pool, "RewriteMap: bad path to dbm map: ", + fname, NULL); + } + + rv = apr_dbm_get_usednames_ex(cmd->pool, newmap->dbmtype, + newmap->datafile, &newmap->checkfile, + &newmap->checkfile2); + if (rv != APR_SUCCESS) { + return apr_pstrcat(cmd->pool, "RewriteMap: dbm type ", + newmap->dbmtype, " is invalid", NULL); + } + } + else if ((strncasecmp(a2, "dbd:", 4) == 0) + || (strncasecmp(a2, "fastdbd:", 8) == 0)) { + if (dbd_prepare == NULL) { + return "RewriteMap types dbd and fastdbd require mod_dbd!"; + } + if ((a2[0] == 'd') || (a2[0] == 'D')) { + newmap->type = MAPTYPE_DBD; + fname = a2+4; + } + else { + newmap->type = MAPTYPE_DBD_CACHE; + fname = a2+8; + newmap->cachename = apr_psprintf(cmd->pool, "%pp:%s", + (void *)cmd->server, a1); + } + newmap->dbdq = a1; + dbd_prepare(cmd->server, fname, newmap->dbdq); + } + else if (strncasecmp(a2, "prg:", 4) == 0) { + apr_tokenize_to_argv(a2 + 4, &newmap->argv, cmd->pool); + + fname = newmap->argv[0]; + if ((newmap->argv[0] = ap_server_root_relative(cmd->pool, + fname)) == NULL) { + return apr_pstrcat(cmd->pool, "RewriteMap: bad path to prg map: ", + fname, NULL); + } + + newmap->type = MAPTYPE_PRG; + newmap->checkfile = newmap->argv[0]; + rewrite_lock_needed = 1; + + if (a3) { + char *tok_cntx; + newmap->user = apr_strtok(apr_pstrdup(cmd->pool, a3), ":", &tok_cntx); + newmap->group = apr_strtok(NULL, ":", &tok_cntx); + } + } + else if (strncasecmp(a2, "int:", 4) == 0) { + newmap->type = MAPTYPE_INT; + newmap->func = (char *(*)(request_rec *,char *)) + apr_hash_get(mapfunc_hash, a2+4, strlen(a2+4)); + if (newmap->func == NULL) { + return apr_pstrcat(cmd->pool, "RewriteMap: internal map not found:", + a2+4, NULL); + } + } + else { + if ((fname = ap_server_root_relative(cmd->pool, a2)) == NULL) { + return apr_pstrcat(cmd->pool, "RewriteMap: bad path to txt map: ", + a2, NULL); + } + + newmap->type = MAPTYPE_TXT; + newmap->datafile = fname; + newmap->checkfile = fname; + newmap->cachename = apr_psprintf(cmd->pool, "%pp:%s", + (void *)cmd->server, a1); + } + + if (newmap->checkfile + && (apr_stat(&st, newmap->checkfile, APR_FINFO_MIN, + cmd->pool) != APR_SUCCESS)) { + return apr_pstrcat(cmd->pool, + "RewriteMap: file for map ", a1, + " not found:", newmap->checkfile, NULL); + } + + apr_hash_set(sconf->rewritemaps, a1, APR_HASH_KEY_STRING, newmap); + + return NULL; +} + +static const char *cmd_rewritebase(cmd_parms *cmd, void *in_dconf, + const char *a1) +{ + rewrite_perdir_conf *dconf = in_dconf; + + if (cmd->path == NULL || dconf == NULL) { + return "RewriteBase: only valid in per-directory config files"; + } + if (a1[0] == '\0') { + return "RewriteBase: empty URL not allowed"; + } + if (a1[0] != '/') { + return "RewriteBase: argument is not a valid URL"; + } + + dconf->baseurl = a1; + dconf->baseurl_set = 1; + + return NULL; +} + +/* + * generic lexer for RewriteRule and RewriteCond flags. + * The parser will be passed in as a function pointer + * and called if a flag was found + */ +static const char *cmd_parseflagfield(apr_pool_t *p, void *cfg, char *key, + const char *(*parse)(apr_pool_t *, + void *, + char *, char *)) +{ + char *val, *nextp, *endp; + const char *err; + + endp = key + strlen(key) - 1; + if (*key != '[' || *endp != ']') { + return "bad flag delimiters"; + } + + *endp = ','; /* for simpler parsing */ + ++key; + + while (*key) { + /* skip leading spaces */ + while (apr_isspace(*key)) { + ++key; + } + + if (!*key || (nextp = ap_strchr(key, ',')) == NULL) { /* NULL should not + * happen, but ... + */ + break; + } + + /* strip trailing spaces */ + endp = nextp - 1; + while (apr_isspace(*endp)) { + --endp; + } + *++endp = '\0'; + + /* split key and val */ + val = ap_strchr(key, '='); + if (val) { + *val++ = '\0'; + } + else { + val = endp; + } + + err = parse(p, cfg, key, val); + if (err) { + return err; + } + + key = nextp + 1; + } + + return NULL; +} + +static const char *cmd_rewritecond_setflag(apr_pool_t *p, void *_cfg, + char *key, char *val) +{ + rewritecond_entry *cfg = _cfg; + + if ( strcasecmp(key, "nocase") == 0 + || strcasecmp(key, "NC") == 0 ) { + cfg->flags |= CONDFLAG_NOCASE; + } + else if ( strcasecmp(key, "ornext") == 0 + || strcasecmp(key, "OR") == 0 ) { + cfg->flags |= CONDFLAG_ORNEXT; + } + else if ( strcasecmp(key, "novary") == 0 + || strcasecmp(key, "NV") == 0 ) { + cfg->flags |= CONDFLAG_NOVARY; + } + else { + return apr_pstrcat(p, "unknown flag '", key, "'", NULL); + } + return NULL; +} + +static const char *cmd_rewritecond(cmd_parms *cmd, void *in_dconf, + const char *in_str) +{ + rewrite_perdir_conf *dconf = in_dconf; + char *str = apr_pstrdup(cmd->pool, in_str); + rewrite_server_conf *sconf; + rewritecond_entry *newcond; + ap_regex_t *regexp; + char *a1 = NULL, *a2 = NULL, *a2_end, *a3 = NULL; + const char *err; + + sconf = ap_get_module_config(cmd->server->module_config, &rewrite_module); + + /* make a new entry in the internal temporary rewrite rule list */ + if (cmd->path == NULL) { /* is server command */ + newcond = apr_array_push(sconf->rewriteconds); + } + else { /* is per-directory command */ + newcond = apr_array_push(dconf->rewriteconds); + } + + /* parse the argument line ourself + * a1 .. a3 are substrings of str, which is a fresh copy + * of the argument line. So we can use a1 .. a3 without + * copying them again. + */ + if (parseargline(str, &a1, &a2, &a2_end, &a3)) { + return apr_pstrcat(cmd->pool, "RewriteCond: bad argument line '", str, + "'", NULL); + } + + /* arg1: the input string */ + newcond->input = a1; + + /* arg3: optional flags field + * (this has to be parsed first, because we need to + * know if the regex should be compiled with ICASE!) + */ + newcond->flags = CONDFLAG_NONE; + if (a3 != NULL) { + if ((err = cmd_parseflagfield(cmd->pool, newcond, a3, + cmd_rewritecond_setflag)) != NULL) { + return apr_pstrcat(cmd->pool, "RewriteCond: ", err, NULL); + } + } + + /* arg2: the pattern */ + newcond->pattern = a2; + if (*a2 == '!') { + newcond->flags |= CONDFLAG_NOTMATCH; + ++a2; + } + + /* determine the pattern type */ + newcond->ptype = CONDPAT_REGEX; + if (strcasecmp(a1, "expr") == 0) { + newcond->ptype = CONDPAT_AP_EXPR; + } + else if (*a2 && a2[1]) { + if (*a2 == '-') { + if (!a2[2]) { + switch (a2[1]) { + case 'f': newcond->ptype = CONDPAT_FILE_EXISTS; break; + case 's': newcond->ptype = CONDPAT_FILE_SIZE; break; + case 'd': newcond->ptype = CONDPAT_FILE_DIR; break; + case 'x': newcond->ptype = CONDPAT_FILE_XBIT; break; + case 'h': newcond->ptype = CONDPAT_FILE_LINK; break; + case 'L': newcond->ptype = CONDPAT_FILE_LINK; break; + case 'l': newcond->ptype = CONDPAT_FILE_LINK; break; + case 'U': newcond->ptype = CONDPAT_LU_URL; break; + case 'F': newcond->ptype = CONDPAT_LU_FILE; break; + } + } + else if (a2[3]) { + switch (a2[1]) { + case 'l': + if (a2[2] == 't') { + a2 += 3; + newcond->ptype = CONDPAT_INT_LT; + } + else if (a2[2] == 'e') { + a2 += 3; + newcond->ptype = CONDPAT_INT_LE; + } + break; + + case 'g': + if (a2[2] == 't') { + a2 += 3; + newcond->ptype = CONDPAT_INT_GT; + } + else if (a2[2] == 'e') { + a2 += 3; + newcond->ptype = CONDPAT_INT_GE; + } + break; + + case 'e': + if (a2[2] == 'q') { + a2 += 3; + newcond->ptype = CONDPAT_INT_EQ; + } + break; + + case 'n': + if (a2[2] == 'e') { + /* Inversion, ensure !-ne == -eq */ + a2 += 3; + newcond->ptype = CONDPAT_INT_EQ; + newcond->flags ^= CONDFLAG_NOTMATCH; + } + break; + } + } + } + else { + switch (*a2) { + case '>': if (*++a2 == '=') + ++a2, newcond->ptype = CONDPAT_STR_GE; + else + newcond->ptype = CONDPAT_STR_GT; + break; + + case '<': if (*++a2 == '=') + ++a2, newcond->ptype = CONDPAT_STR_LE; + else + newcond->ptype = CONDPAT_STR_LT; + break; + + case '=': newcond->ptype = CONDPAT_STR_EQ; + /* "" represents an empty string */ + if (*++a2 == '"' && a2[1] == '"' && !a2[2]) + a2 += 2; + break; + } + } + } + + if ((newcond->ptype != CONDPAT_REGEX) && + (newcond->ptype < CONDPAT_STR_LT || newcond->ptype > CONDPAT_STR_GE) && + (newcond->flags & CONDFLAG_NOCASE)) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00665) + "RewriteCond: NoCase option for non-regex pattern '%s' " + "is not supported and will be ignored. (%s:%d)", a2, + cmd->directive->filename, cmd->directive->line_num); + newcond->flags &= ~CONDFLAG_NOCASE; + } + + newcond->pskip = a2 - newcond->pattern; + newcond->pattern += newcond->pskip; + + if (newcond->ptype == CONDPAT_REGEX) { + regexp = ap_pregcomp(cmd->pool, a2, + AP_REG_EXTENDED | ((newcond->flags & CONDFLAG_NOCASE) + ? AP_REG_ICASE : 0)); + if (!regexp) { + return apr_pstrcat(cmd->pool, "RewriteCond: cannot compile regular " + "expression '", a2, "'", NULL); + } + + newcond->regexp = regexp; + } + else if (newcond->ptype == CONDPAT_AP_EXPR) { + unsigned int flags = newcond->flags & CONDFLAG_NOVARY ? + AP_EXPR_FLAG_DONT_VARY : 0; + newcond->expr = ap_expr_parse_cmd(cmd, a2, flags, &err, NULL); + if (err) + return apr_psprintf(cmd->pool, "RewriteCond: cannot compile " + "expression \"%s\": %s", a2, err); + } + + return NULL; +} + +static const char *cmd_rewriterule_setflag(apr_pool_t *p, void *_cfg, + char *key, char *val) +{ + rewriterule_entry *cfg = _cfg; + int error = 0; + + switch (*key++) { + case 'b': + case 'B': + if (!*key || !strcasecmp(key, "ackrefescaping")) { + cfg->flags |= RULEFLAG_ESCAPEBACKREF; + if (val && *val) { + cfg->escapes = val; + } + } + else if (!strcasecmp(key, "NE")) { + if (val && *val) { + cfg->noescapes = val; + } + else { + return "flag 'BNE' wants a list of characters (i.e. [BNE=...])"; + } + } + else if (!strcasecmp(key, "NP") || !strcasecmp(key, "ackrefernoplus")) { + cfg->flags |= RULEFLAG_ESCAPENOPLUS; + } + else if (!strcasecmp(key, "CTLS")) { + cfg->flags |= RULEFLAG_ESCAPECTLS|RULEFLAG_ESCAPEBACKREF; + } + else { + ++error; + } + break; + case 'c': + case 'C': + if (!*key || !strcasecmp(key, "hain")) { /* chain */ + cfg->flags |= RULEFLAG_CHAIN; + } + else if (((*key == 'O' || *key == 'o') && !key[1]) + || !strcasecmp(key, "ookie")) { /* cookie */ + data_item *cp = cfg->cookie; + + if (!cp) { + cp = cfg->cookie = apr_palloc(p, sizeof(*cp)); + } + else { + while (cp->next) { + cp = cp->next; + } + cp->next = apr_palloc(p, sizeof(*cp)); + cp = cp->next; + } + + cp->next = NULL; + cp->data = val; + } + else { + ++error; + } + break; + case 'd': + case 'D': + if (!*key || !strcasecmp(key, "PI") || !strcasecmp(key,"iscardpath")) { + cfg->flags |= (RULEFLAG_DISCARDPATHINFO); + } + break; + case 'e': + case 'E': + if (!*key || !strcasecmp(key, "nv")) { /* env */ + data_item *cp = cfg->env; + + if (!cp) { + cp = cfg->env = apr_palloc(p, sizeof(*cp)); + } + else { + while (cp->next) { + cp = cp->next; + } + cp->next = apr_palloc(p, sizeof(*cp)); + cp = cp->next; + } + + cp->next = NULL; + cp->data = val; + } + else if (!strcasecmp(key, "nd")) { /* end */ + cfg->flags |= RULEFLAG_END; + } + else { + ++error; + } + break; + + case 'f': + case 'F': + if (!*key || !strcasecmp(key, "orbidden")) { /* forbidden */ + cfg->flags |= (RULEFLAG_STATUS | RULEFLAG_NOSUB); + cfg->forced_responsecode = HTTP_FORBIDDEN; + } + else { + ++error; + } + break; + + case 'g': + case 'G': + if (!*key || !strcasecmp(key, "one")) { /* gone */ + cfg->flags |= (RULEFLAG_STATUS | RULEFLAG_NOSUB); + cfg->forced_responsecode = HTTP_GONE; + } + else { + ++error; + } + break; + + case 'h': + case 'H': + if (!*key || !strcasecmp(key, "andler")) { /* handler */ + cfg->forced_handler = val; + } + else { + ++error; + } + break; + case 'l': + case 'L': + if (!*key || !strcasecmp(key, "ast")) { /* last */ + cfg->flags |= RULEFLAG_LASTRULE; + } + else { + ++error; + } + break; + + case 'n': + case 'N': + if (((*key == 'E' || *key == 'e') && !key[1]) + || !strcasecmp(key, "oescape")) { /* noescape */ + cfg->flags |= RULEFLAG_NOESCAPE; + } + else if (!*key || !strcasecmp(key, "ext")) { /* next */ + cfg->flags |= RULEFLAG_NEWROUND; + if (val && *val) { + cfg->maxrounds = atoi(val); + } + + } + else if (((*key == 'S' || *key == 's') && !key[1]) + || !strcasecmp(key, "osubreq")) { /* nosubreq */ + cfg->flags |= RULEFLAG_IGNOREONSUBREQ; + } + else if (((*key == 'C' || *key == 'c') && !key[1]) + || !strcasecmp(key, "ocase")) { /* nocase */ + cfg->flags |= RULEFLAG_NOCASE; + } + else { + ++error; + } + break; + + case 'p': + case 'P': + if (!*key || !strcasecmp(key, "roxy")) { /* proxy */ + cfg->flags |= RULEFLAG_PROXY; + } + else if (((*key == 'T' || *key == 't') && !key[1]) + || !strcasecmp(key, "assthrough")) { /* passthrough */ + cfg->flags |= RULEFLAG_PASSTHROUGH; + } + else { + ++error; + } + break; + + case 'q': + case 'Q': + if ( !strcasecmp(key, "SA") + || !strcasecmp(key, "sappend")) { /* qsappend */ + cfg->flags |= RULEFLAG_QSAPPEND; + } else if ( !strcasecmp(key, "SD") + || !strcasecmp(key, "sdiscard") ) { /* qsdiscard */ + cfg->flags |= RULEFLAG_QSDISCARD; + } else if ( !strcasecmp(key, "SL") + || !strcasecmp(key, "slast") ) { /* qslast */ + cfg->flags |= RULEFLAG_QSLAST; + } + else { + ++error; + } + break; + + case 'r': + case 'R': + if (!*key || !strcasecmp(key, "edirect")) { /* redirect */ + int status = 0; + + cfg->flags |= RULEFLAG_FORCEREDIRECT; + if (*val) { + if (strcasecmp(val, "permanent") == 0) { + status = HTTP_MOVED_PERMANENTLY; + } + else if (strcasecmp(val, "temp") == 0) { + status = HTTP_MOVED_TEMPORARILY; + } + else if (strcasecmp(val, "seeother") == 0) { + status = HTTP_SEE_OTHER; + } + else if (apr_isdigit(*val)) { + status = atoi(val); + if (status != HTTP_INTERNAL_SERVER_ERROR) { + int idx = + ap_index_of_response(HTTP_INTERNAL_SERVER_ERROR); + + if (ap_index_of_response(status) == idx) { + return apr_psprintf(p, "invalid HTTP " + "response code '%s' for " + "flag 'R'", + val); + } + } + if (!ap_is_HTTP_REDIRECT(status)) { + cfg->flags |= (RULEFLAG_STATUS | RULEFLAG_NOSUB); + } + } + cfg->forced_responsecode = status; + } + } + else { + ++error; + } + break; + + case 's': + case 'S': + if (!*key || !strcasecmp(key, "kip")) { /* skip */ + cfg->skip = atoi(val); + } + else { + ++error; + } + break; + + case 't': + case 'T': + if (!*key || !strcasecmp(key, "ype")) { /* type */ + cfg->forced_mimetype = val; + } + else { + ++error; + } + break; + default: + ++error; + break; + } + + if (error) { + return apr_pstrcat(p, "unknown flag '", --key, "'", NULL); + } + + return NULL; +} + +static const char *cmd_rewriterule(cmd_parms *cmd, void *in_dconf, + const char *in_str) +{ + rewrite_perdir_conf *dconf = in_dconf; + char *str = apr_pstrdup(cmd->pool, in_str); + rewrite_server_conf *sconf; + rewriterule_entry *newrule; + ap_regex_t *regexp; + char *a1 = NULL, *a2 = NULL, *a2_end, *a3 = NULL; + const char *err; + + sconf = ap_get_module_config(cmd->server->module_config, &rewrite_module); + + /* make a new entry in the internal rewrite rule list */ + if (cmd->path == NULL) { /* is server command */ + newrule = apr_array_push(sconf->rewriterules); + } + else { /* is per-directory command */ + newrule = apr_array_push(dconf->rewriterules); + } + + /* parse the argument line ourself */ + if (parseargline(str, &a1, &a2, &a2_end, &a3)) { + return apr_pstrcat(cmd->pool, "RewriteRule: bad argument line '", str, + "'", NULL); + } + + newrule->forced_mimetype = NULL; + newrule->forced_handler = NULL; + newrule->forced_responsecode = HTTP_MOVED_TEMPORARILY; + newrule->flags = RULEFLAG_NONE; + newrule->env = NULL; + newrule->cookie = NULL; + newrule->skip = 0; + newrule->maxrounds = REWRITE_MAX_ROUNDS; + newrule->escapes = newrule->noescapes = NULL; + + /* arg3: optional flags field */ + if (a3 != NULL) { + if ((err = cmd_parseflagfield(cmd->pool, newrule, a3, + cmd_rewriterule_setflag)) != NULL) { + return apr_pstrcat(cmd->pool, "RewriteRule: ", err, NULL); + } + } + + /* arg1: the pattern + * try to compile the regexp to test if is ok + */ + if (*a1 == '!') { + newrule->flags |= RULEFLAG_NOTMATCH; + ++a1; + } + + regexp = ap_pregcomp(cmd->pool, a1, AP_REG_EXTENDED | + ((newrule->flags & RULEFLAG_NOCASE) + ? AP_REG_ICASE : 0)); + if (!regexp) { + return apr_pstrcat(cmd->pool, + "RewriteRule: cannot compile regular expression '", + a1, "'", NULL); + } + + newrule->pattern = a1; + newrule->regexp = regexp; + + /* arg2: the output string */ + newrule->output = a2; + if (*a2 == '-' && !a2[1]) { + newrule->flags |= RULEFLAG_NOSUB; + } + + if (*(a2_end-1) == '?') { + /* a literal ? at the end of the unsubstituted rewrite rule */ + newrule->flags |= RULEFLAG_QSNONE; + *(a2_end-1) = '\0'; /* trailing ? has done its job */ + } + else if (newrule->flags & RULEFLAG_QSDISCARD) { + if (NULL == ap_strchr(newrule->output, '?')) { + newrule->flags |= RULEFLAG_QSNONE; + } + } + + /* now, if the server or per-dir config holds an + * array of RewriteCond entries, we take it for us + * and clear the array + */ + if (cmd->path == NULL) { /* is server command */ + newrule->rewriteconds = sconf->rewriteconds; + sconf->rewriteconds = apr_array_make(cmd->pool, 2, + sizeof(rewritecond_entry)); + } + else { /* is per-directory command */ + newrule->rewriteconds = dconf->rewriteconds; + dconf->rewriteconds = apr_array_make(cmd->pool, 2, + sizeof(rewritecond_entry)); + } + + return NULL; +} + + +/* + * +-------------------------------------------------------+ + * | | + * | the rewriting engine + * | | + * +-------------------------------------------------------+ + */ + +/* Lexicographic Compare */ +static APR_INLINE int compare_lexicography(char *a, char *b) +{ + apr_size_t i, lena, lenb; + + lena = strlen(a); + lenb = strlen(b); + + if (lena == lenb) { + for (i = 0; i < lena; ++i) { + if (a[i] != b[i]) { + return ((unsigned char)a[i] > (unsigned char)b[i]) ? 1 : -1; + } + } + + return 0; + } + + return ((lena > lenb) ? 1 : -1); +} + +/* + * Apply a single rewriteCond + */ +static int apply_rewrite_cond(rewritecond_entry *p, rewrite_ctx *ctx) +{ + char *input = NULL; + apr_finfo_t sb; + request_rec *rsub, *r = ctx->r; + ap_regmatch_t regmatch[AP_MAX_REG_MATCH]; + int rc = 0; + int basis; + + if (p->ptype != CONDPAT_AP_EXPR) + input = do_expand(p->input, ctx, NULL); + + switch (p->ptype) { + case CONDPAT_FILE_EXISTS: + if ( apr_stat(&sb, input, APR_FINFO_MIN, r->pool) == APR_SUCCESS + && sb.filetype == APR_REG) { + rc = 1; + } + break; + + case CONDPAT_FILE_SIZE: + if ( apr_stat(&sb, input, APR_FINFO_MIN, r->pool) == APR_SUCCESS + && sb.filetype == APR_REG && sb.size > 0) { + rc = 1; + } + break; + + case CONDPAT_FILE_LINK: +#if !defined(OS2) + if ( apr_stat(&sb, input, APR_FINFO_MIN | APR_FINFO_LINK, + r->pool) == APR_SUCCESS + && sb.filetype == APR_LNK) { + rc = 1; + } +#endif + break; + + case CONDPAT_FILE_DIR: + if ( apr_stat(&sb, input, APR_FINFO_MIN, r->pool) == APR_SUCCESS + && sb.filetype == APR_DIR) { + rc = 1; + } + break; + + case CONDPAT_FILE_XBIT: + if ( apr_stat(&sb, input, APR_FINFO_PROT, r->pool) == APR_SUCCESS + && (sb.protection & (APR_UEXECUTE | APR_GEXECUTE | APR_WEXECUTE))) { + rc = 1; + } + break; + + case CONDPAT_LU_URL: + if (*input && subreq_ok(r)) { + rsub = ap_sub_req_lookup_uri(input, r, NULL); + if (rsub->status < 400) { + rc = 1; + } + rewritelog((r, 5, NULL, "RewriteCond URI (-U) check: " + "path=%s -> status=%d", input, rsub->status)); + ap_destroy_sub_req(rsub); + } + break; + + case CONDPAT_LU_FILE: + if (*input && subreq_ok(r)) { + rsub = ap_sub_req_lookup_file(input, r, NULL); + if (rsub->status < 300 && + /* double-check that file exists since default result is 200 */ + apr_stat(&sb, rsub->filename, APR_FINFO_MIN, + r->pool) == APR_SUCCESS) { + rc = 1; + } + rewritelog((r, 5, NULL, "RewriteCond file (-F) check: path=%s " + "-> file=%s status=%d", input, rsub->filename, + rsub->status)); + ap_destroy_sub_req(rsub); + } + break; + + case CONDPAT_STR_GE: + basis = 0; + goto test_str_g; + case CONDPAT_STR_GT: + basis = 1; +test_str_g: + if (p->flags & CONDFLAG_NOCASE) { + rc = (strcasecmp(input, p->pattern) >= basis) ? 1 : 0; + } + else { + rc = (compare_lexicography(input, p->pattern) >= basis) ? 1 : 0; + } + break; + + case CONDPAT_STR_LE: + basis = 0; + goto test_str_l; + case CONDPAT_STR_LT: + basis = -1; +test_str_l: + if (p->flags & CONDFLAG_NOCASE) { + rc = (strcasecmp(input, p->pattern) <= basis) ? 1 : 0; + } + else { + rc = (compare_lexicography(input, p->pattern) <= basis) ? 1 : 0; + } + break; + + case CONDPAT_STR_EQ: + /* Note: the only type where the operator is dropped from p->pattern */ + if (p->flags & CONDFLAG_NOCASE) { + rc = !strcasecmp(input, p->pattern); + } + else { + rc = !strcmp(input, p->pattern); + } + break; + + case CONDPAT_INT_GE: rc = (atoi(input) >= atoi(p->pattern)); break; + case CONDPAT_INT_GT: rc = (atoi(input) > atoi(p->pattern)); break; + + case CONDPAT_INT_LE: rc = (atoi(input) <= atoi(p->pattern)); break; + case CONDPAT_INT_LT: rc = (atoi(input) < atoi(p->pattern)); break; + + case CONDPAT_INT_EQ: rc = (atoi(input) == atoi(p->pattern)); break; + + case CONDPAT_AP_EXPR: + { + const char *err, *source; + rc = ap_expr_exec_re(r, p->expr, AP_MAX_REG_MATCH, regmatch, + &source, &err); + if (rc < 0 || err) { + rewritelog((r, 1, ctx->perdir, + "RewriteCond: expr='%s' evaluation failed: %s", + p->pattern - p->pskip, err)); + rc = 0; + } + /* update briRC backref info */ + if (rc && !(p->flags & CONDFLAG_NOTMATCH)) { + ctx->briRC.source = source; + memcpy(ctx->briRC.regmatch, regmatch, sizeof(regmatch)); + } + } + break; + default: + /* it is really a regexp pattern, so apply it */ + rc = !ap_regexec(p->regexp, input, AP_MAX_REG_MATCH, regmatch, 0); + + /* update briRC backref info */ + if (rc && !(p->flags & CONDFLAG_NOTMATCH)) { + ctx->briRC.source = input; + memcpy(ctx->briRC.regmatch, regmatch, sizeof(regmatch)); + } + break; + } + + if (p->flags & CONDFLAG_NOTMATCH) { + rc = !rc; + } + + rewritelog((r, 4, ctx->perdir, "RewriteCond: input='%s' pattern='%s'%s " + "=> %s", input, p->pattern - p->pskip, + (p->flags & CONDFLAG_NOCASE) ? " [NC]" : "", + rc ? "matched" : "not-matched")); + + return rc; +} + +/* check for forced type and handler */ +static APR_INLINE void force_type_handler(rewriterule_entry *p, + rewrite_ctx *ctx) +{ + char *expanded; + + if (p->forced_mimetype) { + expanded = do_expand(p->forced_mimetype, ctx, p); + + if (*expanded) { + ap_str_tolower(expanded); + + rewritelog((ctx->r, 2, ctx->perdir, "remember %s to have MIME-type " + "'%s'", ctx->r->filename, expanded)); + + apr_table_setn(ctx->r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR, + expanded); + } + } + + if (p->forced_handler) { + expanded = do_expand(p->forced_handler, ctx, p); + + if (*expanded) { + ap_str_tolower(expanded); + + rewritelog((ctx->r, 2, ctx->perdir, "remember %s to have " + "Content-handler '%s'", ctx->r->filename, expanded)); + + apr_table_setn(ctx->r->notes, REWRITE_FORCED_HANDLER_NOTEVAR, + expanded); + } + } +} + +/* + * Apply a single RewriteRule + */ +static int apply_rewrite_rule(rewriterule_entry *p, rewrite_ctx *ctx) +{ + ap_regmatch_t regmatch[AP_MAX_REG_MATCH]; + apr_array_header_t *rewriteconds; + rewritecond_entry *conds; + int i, rc; + char *newuri = NULL; + request_rec *r = ctx->r; + int is_proxyreq = 0; + + ctx->uri = r->filename; + + if (ctx->perdir) { + apr_size_t dirlen = strlen(ctx->perdir); + + /* + * Proxy request? + */ + is_proxyreq = ( r->proxyreq && r->filename + && !strncmp(r->filename, "proxy:", 6)); + + /* Since we want to match against the (so called) full URL, we have + * to re-add the PATH_INFO postfix + */ + if (r->path_info && *r->path_info) { + rewritelog((r, 3, ctx->perdir, "add path info postfix: %s -> %s%s", + ctx->uri, ctx->uri, r->path_info)); + ctx->uri = apr_pstrcat(r->pool, ctx->uri, r->path_info, NULL); + } + + /* Additionally we strip the physical path from the url to match + * it independent from the underlying filesystem. + */ + if (!is_proxyreq && strlen(ctx->uri) >= dirlen && + !strncmp(ctx->uri, ctx->perdir, dirlen)) { + + rewritelog((r, 3, ctx->perdir, "strip per-dir prefix: %s -> %s", + ctx->uri, ctx->uri + dirlen)); + ctx->uri = ctx->uri + dirlen; + } + } + + /* Try to match the URI against the RewriteRule pattern + * and exit immediately if it didn't apply. + */ + rewritelog((r, 3, ctx->perdir, "applying pattern '%s' to uri '%s'", + p->pattern, ctx->uri)); + + rc = !ap_regexec(p->regexp, ctx->uri, AP_MAX_REG_MATCH, regmatch, 0); + if (! (( rc && !(p->flags & RULEFLAG_NOTMATCH)) || + (!rc && (p->flags & RULEFLAG_NOTMATCH)) ) ) { + return 0; + } + + /* It matched, wow! Now it's time to prepare the context structure for + * further processing + */ + ctx->vary_this = NULL; + ctx->briRC.source = NULL; + + if (p->flags & RULEFLAG_NOTMATCH) { + ctx->briRR.source = NULL; + } + else { + ctx->briRR.source = apr_pstrdup(r->pool, ctx->uri); + memcpy(ctx->briRR.regmatch, regmatch, sizeof(regmatch)); + } + + /* Ok, we already know the pattern has matched, but we now + * additionally have to check for all existing preconditions + * (RewriteCond) which have to be also true. We do this at + * this very late stage to avoid unnecessary checks which + * would slow down the rewriting engine. + */ + rewriteconds = p->rewriteconds; + conds = (rewritecond_entry *)rewriteconds->elts; + + for (i = 0; i < rewriteconds->nelts; ++i) { + rewritecond_entry *c = &conds[i]; + + rc = apply_rewrite_cond(c, ctx); + /* + * Reset vary_this if the novary flag is set for this condition. + */ + if (c->flags & CONDFLAG_NOVARY) { + ctx->vary_this = NULL; + } + if (c->flags & CONDFLAG_ORNEXT) { + if (!rc) { + /* One condition is false, but another can be still true. */ + ctx->vary_this = NULL; + continue; + } + else { + /* skip the rest of the chained OR conditions */ + while ( i < rewriteconds->nelts + && c->flags & CONDFLAG_ORNEXT) { + c = &conds[++i]; + } + } + } + else if (!rc) { + return 0; + } + + /* If some HTTP header was involved in the condition, remember it + * for later use + */ + if (ctx->vary_this) { + ctx->vary = ctx->vary + ? apr_pstrcat(r->pool, ctx->vary, ", ", ctx->vary_this, + NULL) + : ctx->vary_this; + ctx->vary_this = NULL; + } + } + + /* expand the result */ + if (!(p->flags & RULEFLAG_NOSUB)) { + newuri = do_expand(p->output, ctx, p); + rewritelog((r, 2, ctx->perdir, "rewrite '%s' -> '%s'", ctx->uri, + newuri)); + } + + /* expand [E=var:val] and [CO=<cookie>] */ + do_expand_env(p->env, ctx); + do_expand_cookie(p->cookie, ctx); + + /* non-substitution rules ('RewriteRule <pat> -') end here. */ + if (p->flags & RULEFLAG_NOSUB) { + force_type_handler(p, ctx); + + if (p->flags & RULEFLAG_STATUS) { + rewritelog((r, 2, ctx->perdir, "forcing responsecode %d for %s", + p->forced_responsecode, r->filename)); + + r->status = p->forced_responsecode; + } + + return 2; + } + + /* Now adjust API's knowledge about r->filename and r->args */ + r->filename = newuri; + + if (ctx->perdir && (p->flags & RULEFLAG_DISCARDPATHINFO)) { + r->path_info = NULL; + } + + splitout_queryargs(r, p->flags); + + /* Add the previously stripped per-directory location prefix, unless + * (1) it's an absolute URL path and + * (2) it's a full qualified URL + */ + if ( ctx->perdir && !is_proxyreq && *r->filename != '/' + && !is_absolute_uri(r->filename, NULL)) { + rewritelog((r, 3, ctx->perdir, "add per-dir prefix: %s -> %s%s", + r->filename, ctx->perdir, r->filename)); + + r->filename = apr_pstrcat(r->pool, ctx->perdir, r->filename, NULL); + } + + /* If this rule is forced for proxy throughput + * (`RewriteRule ... ... [P]') then emulate mod_proxy's + * URL-to-filename handler to be sure mod_proxy is triggered + * for this URL later in the Apache API. But make sure it is + * a fully-qualified URL. (If not it is qualified with + * ourself). + */ + if (p->flags & RULEFLAG_PROXY) { + /* For rules evaluated in server context, the mod_proxy fixup + * hook can be relied upon to escape the URI as and when + * necessary, since it occurs later. If in directory context, + * the ordering of the fixup hooks is forced such that + * mod_proxy comes first, so the URI must be escaped here + * instead. See PR 39746, 46428, and other headaches. */ + if (ctx->perdir && (p->flags & RULEFLAG_NOESCAPE) == 0) { + char *old_filename = r->filename; + + r->filename = ap_escape_uri(r->pool, r->filename); + rewritelog((r, 2, ctx->perdir, "escaped URI in per-dir context " + "for proxy, %s -> %s", old_filename, r->filename)); + } + + fully_qualify_uri(r); + + rewritelog((r, 2, ctx->perdir, "forcing proxy-throughput with %s", + r->filename)); + + r->filename = apr_pstrcat(r->pool, "proxy:", r->filename, NULL); + return 1; + } + + /* If this rule is explicitly forced for HTTP redirection + * (`RewriteRule .. .. [R]') then force an external HTTP + * redirect. But make sure it is a fully-qualified URL. (If + * not it is qualified with ourself). + */ + if (p->flags & RULEFLAG_FORCEREDIRECT) { + fully_qualify_uri(r); + + rewritelog((r, 2, ctx->perdir, "explicitly forcing redirect with %s", + r->filename)); + + r->status = p->forced_responsecode; + return 1; + } + + /* Special Rewriting Feature: Self-Reduction + * We reduce the URL by stripping a possible + * http[s]://<ourhost>[:<port>] prefix, i.e. a prefix which + * corresponds to ourself. This is to simplify rewrite maps + * and to avoid recursion, etc. When this prefix is not a + * coincidence then the user has to use [R] explicitly (see + * above). + */ + reduce_uri(r); + + /* If this rule is still implicitly forced for HTTP + * redirection (`RewriteRule .. <scheme>://...') then + * directly force an external HTTP redirect. + */ + if (is_absolute_uri(r->filename, NULL)) { + rewritelog((r, 2, ctx->perdir, "implicitly forcing redirect (rc=%d) " + "with %s", p->forced_responsecode, r->filename)); + + r->status = p->forced_responsecode; + return 1; + } + + /* Finally remember the forced mime-type */ + force_type_handler(p, ctx); + + /* Puuhhhhhhhh... WHAT COMPLICATED STUFF ;_) + * But now we're done for this particular rule. + */ + return 1; +} + +/* + * Apply a complete rule set, + * i.e. a list of rewrite rules + */ +static int apply_rewrite_list(request_rec *r, apr_array_header_t *rewriterules, + char *perdir) +{ + rewriterule_entry *entries; + rewriterule_entry *p; + int i; + int changed; + int rc; + int s; + rewrite_ctx *ctx; + int round = 1; + + ctx = apr_palloc(r->pool, sizeof(*ctx)); + ctx->perdir = perdir; + ctx->r = r; + + /* + * Iterate over all existing rules + */ + entries = (rewriterule_entry *)rewriterules->elts; + changed = 0; + loop: + for (i = 0; i < rewriterules->nelts; i++) { + p = &entries[i]; + + /* + * Ignore this rule on subrequests if we are explicitly + * asked to do so or this is a proxy-throughput or a + * forced redirect rule. + */ + if (r->main != NULL && + (p->flags & RULEFLAG_IGNOREONSUBREQ || + p->flags & RULEFLAG_FORCEREDIRECT )) { + continue; + } + + /* + * Apply the current rule. + */ + ctx->vary = NULL; + rc = apply_rewrite_rule(p, ctx); + + if (rc) { + + /* Catch looping rules with pathinfo growing unbounded */ + if ( strlen( r->filename ) > 2*r->server->limit_req_line ) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "RewriteRule '%s' and URI '%s' " + "exceeded maximum length (%d)", + p->pattern, r->uri, 2*r->server->limit_req_line ); + r->status = HTTP_INTERNAL_SERVER_ERROR; + return ACTION_STATUS; + } + + /* Regardless of what we do next, we've found a match. Check to see + * if any of the request header fields were involved, and add them + * to the Vary field of the response. + */ + if (ctx->vary) { + apr_table_merge(r->headers_out, "Vary", ctx->vary); + } + + /* + * The rule sets the response code (implies match-only) + */ + if (p->flags & RULEFLAG_STATUS) { + return ACTION_STATUS; + } + + /* + * Indicate a change if this was not a match-only rule. + */ + if (rc != 2) { + changed = ((p->flags & RULEFLAG_NOESCAPE) + ? ACTION_NOESCAPE : ACTION_NORMAL); + } + + /* + * Pass-Through Feature (`RewriteRule .. .. [PT]'): + * Because the Apache 1.x API is very limited we + * need this hack to pass the rewritten URL to other + * modules like mod_alias, mod_userdir, etc. + */ + if (p->flags & RULEFLAG_PASSTHROUGH) { + rewritelog((r, 2, perdir, "forcing '%s' to get passed through " + "to next API URI-to-filename handler", r->filename)); + r->filename = apr_pstrcat(r->pool, "passthrough:", + r->filename, NULL); + changed = ACTION_NORMAL; + break; + } + + if (p->flags & RULEFLAG_END) { + rewritelog((r, 8, perdir, "Rule has END flag, no further rewriting for this request")); + apr_pool_userdata_set("1", really_last_key, apr_pool_cleanup_null, r->pool); + break; + } + /* + * Stop processing also on proxy pass-through and + * last-rule and new-round flags. + */ + if (p->flags & (RULEFLAG_PROXY | RULEFLAG_LASTRULE)) { + break; + } + + /* + * On "new-round" flag we just start from the top of + * the rewriting ruleset again. + */ + if (p->flags & RULEFLAG_NEWROUND) { + if (++round >= p->maxrounds) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02596) + "RewriteRule '%s' and URI '%s' exceeded " + "maximum number of rounds (%d) via the [N] flag", + p->pattern, r->uri, p->maxrounds); + + r->status = HTTP_INTERNAL_SERVER_ERROR; + return ACTION_STATUS; + } + goto loop; + } + + /* + * If we are forced to skip N next rules, do it now. + */ + if (p->skip > 0) { + s = p->skip; + while ( i < rewriterules->nelts + && s > 0) { + i++; + s--; + } + } + } + else { + /* + * If current rule is chained with next rule(s), + * skip all this next rule(s) + */ + while ( i < rewriterules->nelts + && p->flags & RULEFLAG_CHAIN) { + i++; + p = &entries[i]; + } + } + } + return changed; +} + + +/* + * +-------------------------------------------------------+ + * | | + * | Module Initialization Hooks + * | | + * +-------------------------------------------------------+ + */ + +static int pre_config(apr_pool_t *pconf, + apr_pool_t *plog, + apr_pool_t *ptemp) +{ + APR_OPTIONAL_FN_TYPE(ap_register_rewrite_mapfunc) *map_pfn_register; + + rewrite_lock_needed = 0; + ap_mutex_register(pconf, rewritemap_mutex_type, NULL, APR_LOCK_DEFAULT, 0); + + /* register int: rewritemap handlers */ + map_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_rewrite_mapfunc); + if (map_pfn_register) { + map_pfn_register("tolower", rewrite_mapfunc_tolower); + map_pfn_register("toupper", rewrite_mapfunc_toupper); + map_pfn_register("escape", rewrite_mapfunc_escape); + map_pfn_register("unescape", rewrite_mapfunc_unescape); + } + dbd_acquire = APR_RETRIEVE_OPTIONAL_FN(ap_dbd_acquire); + dbd_prepare = APR_RETRIEVE_OPTIONAL_FN(ap_dbd_prepare); + return OK; +} + +static int post_config(apr_pool_t *p, + apr_pool_t *plog, + apr_pool_t *ptemp, + server_rec *s) +{ + apr_status_t rv; + + /* check if proxy module is available */ + proxy_available = (ap_find_linked_module("mod_proxy.c") != NULL); + + if (rewrite_lock_needed) { + rv = rewritelock_create(s, p); + if (rv != APR_SUCCESS) { + return HTTP_INTERNAL_SERVER_ERROR; + } + + apr_pool_cleanup_register(p, (void *)s, rewritelock_remove, + apr_pool_cleanup_null); + } + + /* if we are not doing the initial config, step through the servers and + * open the RewriteMap prg:xxx programs, + */ + if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_CONFIG) { + for (; s; s = s->next) { + if (run_rewritemap_programs(s, p) != APR_SUCCESS) { + return HTTP_INTERNAL_SERVER_ERROR; + } + } + } + + return OK; +} + +static void init_child(apr_pool_t *p, server_rec *s) +{ + apr_status_t rv = 0; /* get a rid of gcc warning (REWRITELOG_DISABLED) */ + + if (rewrite_mapr_lock_acquire) { + rv = apr_global_mutex_child_init(&rewrite_mapr_lock_acquire, + apr_global_mutex_lockfile(rewrite_mapr_lock_acquire), p); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(00666) + "mod_rewrite: could not init rewrite_mapr_lock_acquire" + " in child"); + } + } + + /* create the lookup cache */ + if (!init_cache(p)) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(00667) + "mod_rewrite: could not init map cache in child"); + } +} + + +/* + * +-------------------------------------------------------+ + * | | + * | runtime hooks + * | | + * +-------------------------------------------------------+ + */ + +/* + * URI-to-filename hook + * [deals with RewriteRules in server context] + */ +static int hook_uri2file(request_rec *r) +{ + rewrite_perdir_conf *dconf; + rewrite_server_conf *conf; + const char *saved_rulestatus; + const char *var; + const char *thisserver; + char *thisport; + const char *thisurl; + unsigned int port; + int rulestatus; + void *skipdata; + const char *oargs; + + /* + * retrieve the config structures + */ + conf = ap_get_module_config(r->server->module_config, &rewrite_module); + + dconf = (rewrite_perdir_conf *)ap_get_module_config(r->per_dir_config, + &rewrite_module); + + /* + * only do something under runtime if the engine is really enabled, + * else return immediately! + */ + if (!dconf || dconf->state == ENGINE_DISABLED) { + return DECLINED; + } + + /* + * check for the ugly API case of a virtual host section where no + * mod_rewrite directives exists. In this situation we became no chance + * by the API to setup our default per-server config so we have to + * on-the-fly assume we have the default config. But because the default + * config has a disabled rewriting engine we are lucky because can + * just stop operating now. + */ + if (conf->server != r->server) { + return DECLINED; + } + + /* END flag was used as a RewriteRule flag on this request */ + apr_pool_userdata_get(&skipdata, really_last_key, r->pool); + if (skipdata != NULL) { + rewritelog((r, 8, NULL, "Declining, no further rewriting due to END flag")); + return DECLINED; + } + + /* Unless the anyuri option is set, ensure that the input to the + * first rule really is a URL-path, avoiding security issues with + * poorly configured rules. See CVE-2011-3368, CVE-2011-4317. */ + if ((dconf->options & OPTION_ANYURI) == 0 + && ((r->unparsed_uri[0] == '*' && r->unparsed_uri[1] == '\0') + || !r->uri || r->uri[0] != '/')) { + rewritelog((r, 8, NULL, "Declining, request-URI '%s' is not a URL-path. " + "Consult the manual entry for the RewriteOptions directive " + "for options and caveats about matching other strings.", + r->uri)); + return DECLINED; + } + + /* + * remember the original query string for later check, since we don't + * want to apply URL-escaping when no substitution has changed it. + */ + oargs = r->args; + + /* + * add the SCRIPT_URL variable to the env. this is a bit complicated + * due to the fact that apache uses subrequests and internal redirects + */ + + if (r->main == NULL) { + var = apr_table_get(r->subprocess_env, REDIRECT_ENVVAR_SCRIPT_URL); + if (var == NULL) { + apr_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, r->uri); + } + else { + apr_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, var); + } + } + else { + var = apr_table_get(r->main->subprocess_env, ENVVAR_SCRIPT_URL); + apr_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, var); + } + + /* + * create the SCRIPT_URI variable for the env + */ + + /* add the canonical URI of this URL */ + thisserver = ap_get_server_name_for_url(r); + port = ap_get_server_port(r); + if (ap_is_default_port(port, r)) { + thisport = ""; + } + else { + thisport = apr_psprintf(r->pool, ":%u", port); + } + thisurl = apr_table_get(r->subprocess_env, ENVVAR_SCRIPT_URL); + + /* set the variable */ + var = apr_pstrcat(r->pool, ap_http_scheme(r), "://", thisserver, thisport, + thisurl, NULL); + apr_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URI, var); + + if (!(saved_rulestatus = apr_table_get(r->notes,"mod_rewrite_rewritten"))) { + /* if filename was not initially set, + * we start with the requested URI + */ + if (r->filename == NULL) { + r->filename = apr_pstrdup(r->pool, r->uri); + rewritelog((r, 2, NULL, "init rewrite engine with requested uri %s", + r->filename)); + } + else { + rewritelog((r, 2, NULL, "init rewrite engine with passed filename " + "%s. Original uri = %s", r->filename, r->uri)); + } + + /* + * now apply the rules ... + */ + rulestatus = apply_rewrite_list(r, conf->rewriterules, NULL); + apr_table_setn(r->notes, "mod_rewrite_rewritten", + apr_psprintf(r->pool,"%d",rulestatus)); + } + else { + rewritelog((r, 2, NULL, "uri already rewritten. Status %s, Uri %s, " + "r->filename %s", saved_rulestatus, r->uri, r->filename)); + + rulestatus = atoi(saved_rulestatus); + } + + if (rulestatus) { + unsigned skip_absolute = is_absolute_uri(r->filename, NULL); + apr_size_t flen = r->filename ? strlen(r->filename) : 0; + int to_proxyreq = (flen > 6 && strncmp(r->filename, "proxy:", 6) == 0); + int will_escape = skip_absolute && (rulestatus != ACTION_NOESCAPE); + + if (r->args + && !will_escape + && *(ap_scan_vchar_obstext(r->args))) { + /* + * We have a raw control character or a ' ' in r->args. + * Correct encoding was missed. + * Correct encoding was missed and we're not going to escape + * it before returning. + */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10410) + "Rewritten query string contains control " + "characters or spaces"); + return HTTP_FORBIDDEN; + } + + if (ACTION_STATUS == rulestatus) { + int n = r->status; + + r->status = HTTP_OK; + return n; + } + + if (to_proxyreq) { + /* it should be go on as an internal proxy request */ + + /* check if the proxy module is enabled, so + * we can actually use it! + */ + if (!proxy_available) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00669) + "attempt to make remote request from mod_rewrite " + "without proxy enabled: %s", r->filename); + return HTTP_FORBIDDEN; + } + + if (rulestatus == ACTION_NOESCAPE) { + apr_table_setn(r->notes, "proxy-nocanon", "1"); + } + + /* make sure the QUERY_STRING and + * PATH_INFO parts get incorporated + */ + if (r->path_info != NULL) { + r->filename = apr_pstrcat(r->pool, r->filename, + r->path_info, NULL); + } + if ((r->args != NULL) + && ((r->proxyreq == PROXYREQ_PROXY) + || (rulestatus == ACTION_NOESCAPE))) { + /* see proxy_http:proxy_http_canon() */ + r->filename = apr_pstrcat(r->pool, r->filename, + "?", r->args, NULL); + } + + /* now make sure the request gets handled by the proxy handler */ + if (PROXYREQ_NONE == r->proxyreq) { + r->proxyreq = PROXYREQ_REVERSE; + } + r->handler = "proxy-server"; + + rewritelog((r, 1, NULL, "go-ahead with proxy request %s [OK]", + r->filename)); + return OK; + } + else if (skip_absolute > 0) { + int n; + + /* it was finally rewritten to a remote URL */ + + if (rulestatus != ACTION_NOESCAPE) { + rewritelog((r, 1, NULL, "escaping %s for redirect", + r->filename)); + r->filename = escape_absolute_uri(r->pool, r->filename, skip_absolute); + } + + /* append the QUERY_STRING part */ + if (r->args) { + char *escaped_args = NULL; + int noescape = (rulestatus == ACTION_NOESCAPE || + (oargs && !strcmp(r->args, oargs))); + + r->filename = apr_pstrcat(r->pool, r->filename, "?", + noescape + ? r->args + : (escaped_args = + ap_escape_uri(r->pool, r->args)), + NULL); + + rewritelog((r, 1, NULL, "%s %s to query string for redirect %s", + noescape ? "copying" : "escaping", + r->args , + noescape ? "" : escaped_args)); + } + + /* determine HTTP redirect response code */ + if (ap_is_HTTP_REDIRECT(r->status)) { + n = r->status; + r->status = HTTP_OK; /* make Apache kernel happy */ + } + else { + n = HTTP_MOVED_TEMPORARILY; + } + + /* now do the redirection */ + apr_table_setn(r->headers_out, "Location", r->filename); + rewritelog((r, 1, NULL, "redirect to %s [REDIRECT/%d]", r->filename, + n)); + + return n; + } + else if (flen > 12 && strncmp(r->filename, "passthrough:", 12) == 0) { + /* + * Hack because of underpowered API: passing the current + * rewritten filename through to other URL-to-filename handlers + * just as it were the requested URL. This is to enable + * post-processing by mod_alias, etc. which always act on + * r->uri! The difference here is: We do not try to + * add the document root + */ + r->uri = apr_pstrdup(r->pool, r->filename+12); + return DECLINED; + } + else { + /* it was finally rewritten to a local path */ + const char *uri_reduced = NULL; + + /* expand "/~user" prefix */ +#if APR_HAS_USER + r->filename = expand_tildepaths(r, r->filename); +#endif + rewritelog((r, 2, NULL, "local path result: %s", r->filename)); + + /* the filename must be either an absolute local path or an + * absolute local URL. + */ + if ( *r->filename != '/' + && !ap_os_is_path_absolute(r->pool, r->filename)) { + return HTTP_BAD_REQUEST; + } + + /* if there is no valid prefix, we call + * the translator from the core and + * prefix the filename with document_root + * + * NOTICE: + * We cannot leave out the prefix_stat because + * - when we always prefix with document_root + * then no absolute path can be created, e.g. via + * emulating a ScriptAlias directive, etc. + * - when we always NOT prefix with document_root + * then the files under document_root have to + * be references directly and document_root + * gets never used and will be a dummy parameter - + * this is also bad + * + * BUT: + * Under real Unix systems this is no problem, + * because we only do stat() on the first directory + * and this gets cached by the kernel for along time! + */ + + if(!(conf->options & OPTION_LEGACY_PREFIX_DOCROOT)) { + uri_reduced = apr_table_get(r->notes, "mod_rewrite_uri_reduced"); + } + + if (!prefix_stat(r->filename, r->pool) || uri_reduced != NULL) { + int res; + char *tmp = r->uri; + + r->uri = r->filename; + res = ap_core_translate(r); + r->uri = tmp; + + if (res != OK) { + rewritelog((r, 1, NULL, "prefixing with document_root of %s" + " FAILED", r->filename)); + + return res; + } + + rewritelog((r, 2, NULL, "prefixed with document_root to %s", + r->filename)); + } + + rewritelog((r, 1, NULL, "go-ahead with %s [OK]", r->filename)); + return OK; + } + } + else { + rewritelog((r, 1, NULL, "pass through %s", r->filename)); + return DECLINED; + } +} + +/* + * Fixup hook + * [RewriteRules in directory context] + */ +static int hook_fixup(request_rec *r) +{ + rewrite_perdir_conf *dconf; + char *cp; + char *cp2; + const char *ccp; + apr_size_t l; + int rulestatus; + int n; + char *ofilename, *oargs; + int is_proxyreq; + void *skipdata; + + dconf = (rewrite_perdir_conf *)ap_get_module_config(r->per_dir_config, + &rewrite_module); + + /* if there is no per-dir config we return immediately */ + if (dconf == NULL) { + return DECLINED; + } + + /* + * only do something under runtime if the engine is really enabled, + * for this directory, else return immediately! + */ + if (dconf->state == ENGINE_DISABLED) { + return DECLINED; + } + + /* if there are no real (i.e. no RewriteRule directives!) + per-dir config of us, we return also immediately */ + if (dconf->directory == NULL) { + return DECLINED; + } + + /* + * Proxy request? + */ + is_proxyreq = ( r->proxyreq && r->filename + && !strncmp(r->filename, "proxy:", 6)); + + /* + * .htaccess file is called before really entering the directory, i.e.: + * URL: http://localhost/foo and .htaccess is located in foo directory + * Ignore such attempts, allowing mod_dir to direct the client to the + * canonical URL. This can be controlled with the AllowNoSlash option. + */ + if (!is_proxyreq && !(dconf->options & OPTION_NOSLASH)) { + l = strlen(dconf->directory) - 1; + if (r->filename && strlen(r->filename) == l && + (dconf->directory)[l] == '/' && + !strncmp(r->filename, dconf->directory, l)) { + return DECLINED; + } + } + + /* END flag was used as a RewriteRule flag on this request */ + apr_pool_userdata_get(&skipdata, really_last_key, r->pool); + if (skipdata != NULL) { + rewritelog((r, 8, dconf->directory, "Declining, no further rewriting due to END flag")); + return DECLINED; + } + + /* + * Do the Options check after engine check, so + * the user is able to explicitly turn RewriteEngine Off. + */ + if (!(ap_allow_options(r) & (OPT_SYM_LINKS | OPT_SYM_OWNER))) { + /* FollowSymLinks is mandatory! */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00670) + "Options FollowSymLinks and SymLinksIfOwnerMatch are both off, " + "so the RewriteRule directive is also forbidden " + "due to its similar ability to circumvent directory restrictions : " + "%s", r->filename); + return HTTP_FORBIDDEN; + } + + /* + * remember the current filename before rewriting for later check + * to prevent deadlooping because of internal redirects + * on final URL/filename which can be equal to the initial one. + * also, we'll restore original r->filename if we decline this + * request + */ + ofilename = r->filename; + oargs = r->args; + + if (r->filename == NULL) { + r->filename = apr_pstrdup(r->pool, r->uri); + rewritelog((r, 2, dconf->directory, "init rewrite engine with" + " requested uri %s", r->filename)); + } + + /* + * now apply the rules ... + */ + rulestatus = apply_rewrite_list(r, dconf->rewriterules, dconf->directory); + if (rulestatus) { + unsigned skip_absolute = is_absolute_uri(r->filename, NULL); + int to_proxyreq = 0; + int will_escape = 0; + + l = strlen(r->filename); + to_proxyreq = l > 6 && strncmp(r->filename, "proxy:", 6) == 0; + will_escape = skip_absolute && (rulestatus != ACTION_NOESCAPE); + + if (r->args + && !will_escape + && *(ap_scan_vchar_obstext(r->args))) { + /* + * We have a raw control character or a ' ' in r->args. + * Correct encoding was missed. + */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10411) + "Rewritten query string contains control " + "characters or spaces"); + return HTTP_FORBIDDEN; + } + + if (ACTION_STATUS == rulestatus) { + int n = r->status; + + r->status = HTTP_OK; + return n; + } + + if (to_proxyreq) { + /* it should go on as an internal proxy request */ + + /* make sure the QUERY_STRING and + * PATH_INFO parts get incorporated + * (r->path_info was already appended by the + * rewriting engine because of the per-dir context!) + */ + if (r->args != NULL) { + /* see proxy_http:proxy_http_canon() */ + r->filename = apr_pstrcat(r->pool, r->filename, + "?", r->args, NULL); + } + + /* now make sure the request gets handled by the proxy handler */ + if (PROXYREQ_NONE == r->proxyreq) { + r->proxyreq = PROXYREQ_REVERSE; + } + r->handler = "proxy-server"; + + rewritelog((r, 1, dconf->directory, "go-ahead with proxy request " + "%s [OK]", r->filename)); + return OK; + } + else if (skip_absolute > 0) { + /* it was finally rewritten to a remote URL */ + + /* because we are in a per-dir context + * first try to replace the directory with its base-URL + * if there is a base-URL available + */ + if (dconf->baseurl != NULL) { + /* skip 'scheme://' */ + cp = r->filename + skip_absolute; + + if ((cp = ap_strchr(cp, '/')) != NULL && *(++cp)) { + rewritelog((r, 2, dconf->directory, + "trying to replace prefix %s with %s", + dconf->directory, dconf->baseurl)); + + /* I think, that hack needs an explanation: + * well, here is it: + * mod_rewrite was written for unix systems, were + * absolute file-system paths start with a slash. + * URL-paths _also_ start with slashes, so they + * can be easily compared with system paths. + * + * the following assumes, that the actual url-path + * may be prefixed by the current directory path and + * tries to replace the system path with the RewriteBase + * URL. + * That assumption is true if we use a RewriteRule like + * + * RewriteRule ^foo bar [R] + * + * (see apply_rewrite_rule function) + * However on systems that don't have a / as system + * root this will never match, so we skip the / after the + * hostname and compare/substitute only the stuff after it. + * + * (note that cp was already increased to the right value) + */ + cp2 = subst_prefix_path(r, cp, (*dconf->directory == '/') + ? dconf->directory + 1 + : dconf->directory, + dconf->baseurl + 1); + if (strcmp(cp2, cp) != 0) { + *cp = '\0'; + r->filename = apr_pstrcat(r->pool, r->filename, + cp2, NULL); + } + } + } + + /* now prepare the redirect... */ + if (rulestatus != ACTION_NOESCAPE) { + rewritelog((r, 1, dconf->directory, "escaping %s for redirect", + r->filename)); + r->filename = escape_absolute_uri(r->pool, r->filename, skip_absolute); + } + + /* append the QUERY_STRING part */ + if (r->args) { + char *escaped_args = NULL; + int noescape = (rulestatus == ACTION_NOESCAPE || + (oargs && !strcmp(r->args, oargs))); + + r->filename = apr_pstrcat(r->pool, r->filename, "?", + noescape + ? r->args + : (escaped_args = ap_escape_uri(r->pool, r->args)), + NULL); + + rewritelog((r, 1, dconf->directory, "%s %s to query string for redirect %s", + noescape ? "copying" : "escaping", + r->args , + noescape ? "" : escaped_args)); + } + + /* determine HTTP redirect response code */ + if (ap_is_HTTP_REDIRECT(r->status)) { + n = r->status; + r->status = HTTP_OK; /* make Apache kernel happy */ + } + else { + n = HTTP_MOVED_TEMPORARILY; + } + + /* now do the redirection */ + apr_table_setn(r->headers_out, "Location", r->filename); + rewritelog((r, 1, dconf->directory, "redirect to %s [REDIRECT/%d]", + r->filename, n)); + return n; + } + else { + const char *tmpfilename = NULL; + /* it was finally rewritten to a local path */ + + /* if someone used the PASSTHROUGH flag in per-dir + * context we just ignore it. It is only useful + * in per-server context + */ + if (l > 12 && strncmp(r->filename, "passthrough:", 12) == 0) { + r->filename = apr_pstrdup(r->pool, r->filename+12); + } + + /* the filename must be either an absolute local path or an + * absolute local URL. + */ + if ( *r->filename != '/' + && !ap_os_is_path_absolute(r->pool, r->filename)) { + return HTTP_BAD_REQUEST; + } + + /* Check for deadlooping: + * At this point we KNOW that at least one rewriting + * rule was applied, but when the resulting URL is + * the same as the initial URL, we are not allowed to + * use the following internal redirection stuff because + * this would lead to a deadloop. + */ + if (ofilename != NULL && strcmp(r->filename, ofilename) == 0) { + rewritelog((r, 1, dconf->directory, "initial URL equal rewritten" + " URL: %s [IGNORING REWRITE]", r->filename)); + return OK; + } + + tmpfilename = r->filename; + + /* if there is a valid base-URL then substitute + * the per-dir prefix with this base-URL if the + * current filename still is inside this per-dir + * context. If not then treat the result as a + * plain URL + */ + if (dconf->baseurl != NULL) { + rewritelog((r, 2, dconf->directory, "trying to replace prefix " + "%s with %s", dconf->directory, dconf->baseurl)); + + r->filename = subst_prefix_path(r, r->filename, + dconf->directory, + dconf->baseurl); + } + else { + /* if no explicit base-URL exists we assume + * that the directory prefix is also a valid URL + * for this webserver and only try to remove the + * document_root if it is prefix + */ + if ((ccp = ap_document_root(r)) != NULL) { + /* strip trailing slash */ + l = strlen(ccp); + if (ccp[l-1] == '/') { + --l; + } + if (!strncmp(r->filename, ccp, l) && + r->filename[l] == '/') { + rewritelog((r, 2,dconf->directory, "strip document_root" + " prefix: %s -> %s", r->filename, + r->filename+l)); + + r->filename = apr_pstrdup(r->pool, r->filename+l); + } + } + } + + /* No base URL, or r->filename wasn't still under dconf->directory + * or, r->filename wasn't still under the document root. + * If there's a context document root AND a context prefix, and + * the context document root is a prefix of r->filename, replace. + * This allows a relative substitution on a path found by mod_userdir + * or mod_alias without baking in a RewriteBase. + */ + if (tmpfilename == r->filename && + !(dconf->options & OPTION_IGNORE_CONTEXT_INFO)) { + if ((ccp = ap_context_document_root(r)) != NULL) { + const char *prefix = ap_context_prefix(r); + if (prefix != NULL) { + rewritelog((r, 2, dconf->directory, "trying to replace " + "context docroot %s with context prefix %s", + ccp, prefix)); + r->filename = subst_prefix_path(r, r->filename, + ccp, prefix); + } + } + } + + apr_table_setn(r->notes, "redirect-keeps-vary", ""); + + /* now initiate the internal redirect */ + rewritelog((r, 1, dconf->directory, "internal redirect with %s " + "[INTERNAL REDIRECT]", r->filename)); + r->filename = apr_pstrcat(r->pool, "redirect:", r->filename, NULL); + r->handler = REWRITE_REDIRECT_HANDLER_NAME; + return OK; + } + } + else { + rewritelog((r, 1, dconf->directory, "pass through %s", r->filename)); + r->filename = ofilename; + return DECLINED; + } +} + +/* + * MIME-type hook + * [T=...,H=...] execution + */ +static int hook_mimetype(request_rec *r) +{ + const char *t; + + /* type */ + t = apr_table_get(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR); + if (t && *t) { + rewritelog((r, 1, NULL, "force filename %s to have MIME-type '%s'", + r->filename, t)); + + ap_set_content_type(r, t); + } + + /* handler */ + t = apr_table_get(r->notes, REWRITE_FORCED_HANDLER_NOTEVAR); + if (t && *t) { + rewritelog((r, 1, NULL, "force filename %s to have the " + "Content-handler '%s'", r->filename, t)); + + r->handler = t; + } + + return OK; +} + + +/* + * "content" handler for internal redirects + */ +static int handler_redirect(request_rec *r) +{ + if (strcmp(r->handler, REWRITE_REDIRECT_HANDLER_NAME)) { + return DECLINED; + } + + /* just make sure that we are really meant! */ + if (strncmp(r->filename, "redirect:", 9) != 0) { + return DECLINED; + } + + /* now do the internal redirect */ + ap_internal_redirect(apr_pstrcat(r->pool, r->filename+9, + r->args ? "?" : NULL, r->args, NULL), r); + + /* and return gracefully */ + return OK; +} + + +/* + * +-------------------------------------------------------+ + * | | + * | Module paraphernalia + * | | + * +-------------------------------------------------------+ + */ + +static const command_rec command_table[] = { + AP_INIT_FLAG( "RewriteEngine", cmd_rewriteengine, NULL, OR_FILEINFO, + "On or Off to enable or disable (default) the whole " + "rewriting engine"), + AP_INIT_ITERATE( "RewriteOptions", cmd_rewriteoptions, NULL, OR_FILEINFO, + "List of option strings to set"), + AP_INIT_TAKE1( "RewriteBase", cmd_rewritebase, NULL, OR_FILEINFO, + "the base URL of the per-directory context"), + AP_INIT_RAW_ARGS("RewriteCond", cmd_rewritecond, NULL, OR_FILEINFO, + "an input string and a to be applied regexp-pattern"), + AP_INIT_RAW_ARGS("RewriteRule", cmd_rewriterule, NULL, OR_FILEINFO, + "an URL-applied regexp-pattern and a substitution URL"), + AP_INIT_TAKE23( "RewriteMap", cmd_rewritemap, NULL, RSRC_CONF, + "a mapname and a filename and options"), + { NULL } +}; + +static void ap_register_rewrite_mapfunc(char *name, rewrite_mapfunc_t *func) +{ + apr_hash_set(mapfunc_hash, name, strlen(name), (const void *)func); +} + +static void register_hooks(apr_pool_t *p) +{ + /* fixup after mod_proxy, so that the proxied url will not + * escaped accidentally by mod_proxy's fixup. + */ + static const char * const aszPre[]={ "mod_proxy.c", NULL }; + + /* make the hashtable before registering the function, so that + * other modules are prevented from accessing uninitialized memory. + */ + mapfunc_hash = apr_hash_make(p); + APR_REGISTER_OPTIONAL_FN(ap_register_rewrite_mapfunc); + + ap_hook_handler(handler_redirect, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_pre_config(pre_config, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_post_config(post_config, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_child_init(init_child, NULL, NULL, APR_HOOK_MIDDLE); + + ap_hook_fixups(hook_fixup, aszPre, NULL, APR_HOOK_FIRST); + ap_hook_fixups(hook_mimetype, NULL, NULL, APR_HOOK_LAST); + ap_hook_translate_name(hook_uri2file, NULL, NULL, APR_HOOK_FIRST); +} + + /* the main config structure */ +AP_DECLARE_MODULE(rewrite) = { + STANDARD20_MODULE_STUFF, + config_perdir_create, /* create per-dir config structures */ + config_perdir_merge, /* merge per-dir config structures */ + config_server_create, /* create per-server config structures */ + config_server_merge, /* merge per-server config structures */ + command_table, /* table of config file commands */ + register_hooks /* register hooks */ +}; + +/*EOF*/ diff --git a/modules/mappers/mod_rewrite.dep b/modules/mappers/mod_rewrite.dep new file mode 100644 index 0000000..b6956b1 --- /dev/null +++ b/modules/mappers/mod_rewrite.dep @@ -0,0 +1,65 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_rewrite.mak + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + + +.\mod_rewrite.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\http_vhost.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_mutex.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_dbd.h"\ + "..\..\srclib\apr-util\include\apr_dbm.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_signal.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + "..\database\mod_dbd.h"\ + "..\ssl\mod_ssl.h"\ + ".\mod_rewrite.h"\ + diff --git a/modules/mappers/mod_rewrite.dsp b/modules/mappers/mod_rewrite.dsp new file mode 100644 index 0000000..adde8ee --- /dev/null +++ b/modules/mappers/mod_rewrite.dsp @@ -0,0 +1,111 @@ +# Microsoft Developer Studio Project File - Name="mod_rewrite" - 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_rewrite - 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_rewrite.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_rewrite.mak" CFG="mod_rewrite - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_rewrite - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_rewrite - 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_rewrite - 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 "../database" /I "../ssl" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_rewrite_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_rewrite.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_rewrite.so" /d LONG_NAME="rewrite_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_rewrite.so" /base:@..\..\os\win32\BaseAddr.ref,mod_rewrite.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_rewrite.so" /base:@..\..\os\win32\BaseAddr.ref,mod_rewrite.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_rewrite.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_rewrite - 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 "../database" /I "../ssl" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_rewrite_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_rewrite.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_rewrite.so" /d LONG_NAME="rewrite_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_rewrite.so" /base:@..\..\os\win32\BaseAddr.ref,mod_rewrite.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_rewrite.so" /base:@..\..\os\win32\BaseAddr.ref,mod_rewrite.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_rewrite.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_rewrite - Win32 Release" +# Name "mod_rewrite - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_rewrite.c +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/mappers/mod_rewrite.exp b/modules/mappers/mod_rewrite.exp new file mode 100644 index 0000000..8f2165b --- /dev/null +++ b/modules/mappers/mod_rewrite.exp @@ -0,0 +1 @@ +rewrite_module diff --git a/modules/mappers/mod_rewrite.h b/modules/mappers/mod_rewrite.h new file mode 100644 index 0000000..8fa158e --- /dev/null +++ b/modules/mappers/mod_rewrite.h @@ -0,0 +1,42 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file mod_rewrite.h + * @brief Rewrite Extension module for Apache + * + * @defgroup MOD_REWRITE mod_rewrite + * @ingroup APACHE_MODS + * @{ + */ + +#ifndef MOD_REWRITE_H +#define MOD_REWRITE_H 1 + +#include "apr_optional.h" +#include "httpd.h" + +#define REWRITE_REDIRECT_HANDLER_NAME "redirect-handler" + +/* rewrite map function prototype */ +typedef char *(rewrite_mapfunc_t)(request_rec *r, char *key); + +/* optional function declaration */ +APR_DECLARE_OPTIONAL_FN(void, ap_register_rewrite_mapfunc, + (char *name, rewrite_mapfunc_t *func)); + +#endif /* MOD_REWRITE_H */ +/** @} */ diff --git a/modules/mappers/mod_rewrite.mak b/modules/mappers/mod_rewrite.mak new file mode 100644 index 0000000..3b08cab --- /dev/null +++ b/modules/mappers/mod_rewrite.mak @@ -0,0 +1,353 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_rewrite.dsp +!IF "$(CFG)" == "" +CFG=mod_rewrite - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_rewrite - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_rewrite - Win32 Release" && "$(CFG)" != "mod_rewrite - 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_rewrite.mak" CFG="mod_rewrite - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_rewrite - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_rewrite - 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_rewrite - 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_rewrite.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_rewrite.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_rewrite.obj" + -@erase "$(INTDIR)\mod_rewrite.res" + -@erase "$(INTDIR)\mod_rewrite_src.idb" + -@erase "$(INTDIR)\mod_rewrite_src.pdb" + -@erase "$(OUTDIR)\mod_rewrite.exp" + -@erase "$(OUTDIR)\mod_rewrite.lib" + -@erase "$(OUTDIR)\mod_rewrite.pdb" + -@erase "$(OUTDIR)\mod_rewrite.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../database" /I "../ssl" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_rewrite_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_rewrite.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_rewrite.so" /d LONG_NAME="rewrite_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_rewrite.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_rewrite.pdb" /debug /out:"$(OUTDIR)\mod_rewrite.so" /implib:"$(OUTDIR)\mod_rewrite.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_rewrite.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_rewrite.obj" \ + "$(INTDIR)\mod_rewrite.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" + +"$(OUTDIR)\mod_rewrite.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_rewrite.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_rewrite.so" + if exist .\Release\mod_rewrite.so.manifest mt.exe -manifest .\Release\mod_rewrite.so.manifest -outputresource:.\Release\mod_rewrite.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_rewrite - 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_rewrite.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_rewrite.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_rewrite.obj" + -@erase "$(INTDIR)\mod_rewrite.res" + -@erase "$(INTDIR)\mod_rewrite_src.idb" + -@erase "$(INTDIR)\mod_rewrite_src.pdb" + -@erase "$(OUTDIR)\mod_rewrite.exp" + -@erase "$(OUTDIR)\mod_rewrite.lib" + -@erase "$(OUTDIR)\mod_rewrite.pdb" + -@erase "$(OUTDIR)\mod_rewrite.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../database" /I "../ssl" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_rewrite_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_rewrite.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_rewrite.so" /d LONG_NAME="rewrite_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_rewrite.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_rewrite.pdb" /debug /out:"$(OUTDIR)\mod_rewrite.so" /implib:"$(OUTDIR)\mod_rewrite.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_rewrite.so +LINK32_OBJS= \ + "$(INTDIR)\mod_rewrite.obj" \ + "$(INTDIR)\mod_rewrite.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" + +"$(OUTDIR)\mod_rewrite.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_rewrite.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_rewrite.so" + if exist .\Debug\mod_rewrite.so.manifest mt.exe -manifest .\Debug\mod_rewrite.so.manifest -outputresource:.\Debug\mod_rewrite.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_rewrite.dep") +!INCLUDE "mod_rewrite.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_rewrite.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_rewrite - Win32 Release" || "$(CFG)" == "mod_rewrite - Win32 Debug" + +!IF "$(CFG)" == "mod_rewrite - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\mappers" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_rewrite - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\mappers" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ENDIF + +!IF "$(CFG)" == "mod_rewrite - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\mappers" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_rewrite - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\mappers" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ENDIF + +!IF "$(CFG)" == "mod_rewrite - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\mappers" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_rewrite - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\mappers" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\mappers" + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_rewrite - Win32 Release" + + +"$(INTDIR)\mod_rewrite.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_rewrite.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_rewrite.so" /d LONG_NAME="rewrite_module for Apache" $(SOURCE) + + +!ELSEIF "$(CFG)" == "mod_rewrite - Win32 Debug" + + +"$(INTDIR)\mod_rewrite.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_rewrite.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_rewrite.so" /d LONG_NAME="rewrite_module for Apache" $(SOURCE) + + +!ENDIF + +SOURCE=.\mod_rewrite.c + +"$(INTDIR)\mod_rewrite.obj" : $(SOURCE) "$(INTDIR)" + + + +!ENDIF + diff --git a/modules/mappers/mod_speling.c b/modules/mappers/mod_speling.c new file mode 100644 index 0000000..2ed65eb --- /dev/null +++ b/modules/mappers/mod_speling.c @@ -0,0 +1,528 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "apr.h" +#include "apr_file_io.h" +#include "apr_strings.h" +#include "apr_lib.h" + +#define APR_WANT_STRFUNC +#include "apr_want.h" + +#include "httpd.h" +#include "http_core.h" +#include "http_config.h" +#include "http_request.h" +#include "http_log.h" + +/* mod_speling.c - by Alexei Kosut <akosut@organic.com> June, 1996 + * + * This module is transparent, and simple. It attempts to correct + * misspellings of URLs that users might have entered, namely by checking + * capitalizations. If it finds a match, it sends a redirect. + * + * Sep-1999 Hugo Haas <hugo@w3.org> + * o Added a CheckCaseOnly option to check only miscapitalized words. + * + * 08-Aug-1997 <Martin.Kraemer@Mch.SNI.De> + * o Upgraded module interface to apache_1.3a2-dev API (more NULL's in + * speling_module). + * o Integrated tcsh's "spelling correction" routine which allows one + * misspelling (character insertion/omission/typo/transposition). + * Rewrote it to ignore case as well. This ought to catch the majority + * of misspelled requests. + * o Commented out the second pass where files' suffixes are stripped. + * Given the better hit rate of the first pass, this rather ugly + * (request index.html, receive index.db ?!?!) solution can be + * omitted. + * o wrote a "kind of" html page for mod_speling + * + * Activate it with "CheckSpelling On" + */ + +module AP_MODULE_DECLARE_DATA speling_module; + +typedef struct { + int enabled; + int check_case_only; + int check_basename_match; +} spconfig; + +/* + * Create a configuration specific to this module for a server or directory + * location, and fill it with the default settings. + * + * The API says that in the absence of a merge function, the record for the + * closest ancestor is used exclusively. That's what we want, so we don't + * bother to have such a function. + */ + +static void *mkconfig(apr_pool_t *p) +{ + spconfig *cfg = apr_pcalloc(p, sizeof(spconfig)); + + cfg->enabled = 0; + cfg->check_case_only = 0; + cfg->check_basename_match = 1; + return cfg; +} + +/* + * Respond to a callback to create configuration record for a server or + * vhost environment. + */ +static void *create_mconfig_for_server(apr_pool_t *p, server_rec *s) +{ + return mkconfig(p); +} + +/* + * Respond to a callback to create a config record for a specific directory. + */ +static void *create_mconfig_for_directory(apr_pool_t *p, char *dir) +{ + return mkconfig(p); +} + +/* + * Define the directives specific to this module. This structure is referenced + * later by the 'module' structure. + */ +static const command_rec speling_cmds[] = +{ + AP_INIT_FLAG("CheckSpelling", ap_set_flag_slot, + (void*)APR_OFFSETOF(spconfig, enabled), OR_OPTIONS, + "whether or not to fix miscapitalized/misspelled requests"), + AP_INIT_FLAG("CheckCaseOnly", ap_set_flag_slot, + (void*)APR_OFFSETOF(spconfig, check_case_only), OR_OPTIONS, + "whether or not to fix only miscapitalized requests"), + AP_INIT_FLAG("CheckBasenameMatch", ap_set_flag_slot, + (void*)APR_OFFSETOF(spconfig, check_basename_match), OR_OPTIONS, + "whether or not to fix files with the same base name"), + { NULL } +}; + +typedef enum { + SP_IDENTICAL = 0, + SP_MISCAPITALIZED = 1, + SP_TRANSPOSITION = 2, + SP_MISSINGCHAR = 3, + SP_EXTRACHAR = 4, + SP_SIMPLETYPO = 5, + SP_VERYDIFFERENT = 6 +} sp_reason; + +static const char *sp_reason_str[] = +{ + "identical", + "miscapitalized", + "transposed characters", + "character missing", + "extra character", + "mistyped character", + "common basename", +}; + +typedef struct { + const char *name; + sp_reason quality; +} misspelled_file; + +/* + * spdist() is taken from Kernighan & Pike, + * _The_UNIX_Programming_Environment_ + * and adapted somewhat to correspond better to psychological reality. + * (Note the changes to the return values) + * + * According to Pollock and Zamora, CACM April 1984 (V. 27, No. 4), + * page 363, the correct order for this is: + * OMISSION = TRANSPOSITION > INSERTION > SUBSTITUTION + * thus, it was exactly backwards in the old version. -- PWP + * + * This routine was taken out of tcsh's spelling correction code + * (tcsh-6.07.04) and re-converted to apache data types ("char" type + * instead of tcsh's NLS'ed "Char"). Plus it now ignores the case + * during comparisons, so is a "approximate strcasecmp()". + * NOTE that is still allows only _one_ real "typo", + * it does NOT try to correct multiple errors. + */ + +static sp_reason spdist(const char *s, const char *t) +{ + for (; apr_tolower(*s) == apr_tolower(*t); t++, s++) { + if (*t == '\0') { + return SP_MISCAPITALIZED; /* exact match (sans case) */ + } + } + if (*s) { + if (*t) { + if (s[1] && t[1] && apr_tolower(*s) == apr_tolower(t[1]) + && apr_tolower(*t) == apr_tolower(s[1]) + && strcasecmp(s + 2, t + 2) == 0) { + return SP_TRANSPOSITION; /* transposition */ + } + if (strcasecmp(s + 1, t + 1) == 0) { + return SP_SIMPLETYPO; /* 1 char mismatch */ + } + } + if (strcasecmp(s + 1, t) == 0) { + return SP_EXTRACHAR; /* extra character */ + } + } + if (*t && strcasecmp(s, t + 1) == 0) { + return SP_MISSINGCHAR; /* missing character */ + } + return SP_VERYDIFFERENT; /* distance too large to fix. */ +} + +static int sort_by_quality(const void *left, const void *rite) +{ + return (int) (((misspelled_file *) left)->quality) + - (int) (((misspelled_file *) rite)->quality); +} + +static int check_speling(request_rec *r) +{ + spconfig *cfg; + char *good, *bad, *postgood, *url; + apr_finfo_t dirent; + int filoc, dotloc, urlen, pglen; + apr_array_header_t *candidates = NULL; + apr_dir_t *dir; + + cfg = ap_get_module_config(r->per_dir_config, &speling_module); + if (!cfg->enabled) { + return DECLINED; + } + + /* We only want to worry about GETs */ + if (r->method_number != M_GET) { + return DECLINED; + } + + /* We've already got a file of some kind or another */ + if (r->finfo.filetype != APR_NOFILE) { + return DECLINED; + } + + /* Not a file request */ + if (r->proxyreq || !r->filename) { + return DECLINED; + } + + /* This is a sub request - don't mess with it */ + if (r->main) { + return DECLINED; + } + + /* + * The request should end up looking like this: + * r->uri: /correct-url/mispelling/more + * r->filename: /correct-file/mispelling r->path_info: /more + * + * So we do this in steps. First break r->filename into two pieces + */ + + filoc = ap_rind(r->filename, '/'); + /* + * Don't do anything if the request doesn't contain a slash, or + * requests "/" + */ + if (filoc == -1 || strcmp(r->uri, "/") == 0) { + return DECLINED; + } + + /* good = /correct-file */ + good = apr_pstrndup(r->pool, r->filename, filoc); + /* bad = mispelling */ + bad = apr_pstrdup(r->pool, r->filename + filoc + 1); + /* postgood = mispelling/more */ + postgood = apr_pstrcat(r->pool, bad, r->path_info, NULL); + + urlen = strlen(r->uri); + pglen = strlen(postgood); + + /* Check to see if the URL pieces add up */ + if (strcmp(postgood, r->uri + (urlen - pglen))) { + return DECLINED; + } + + /* url = /correct-url */ + url = apr_pstrndup(r->pool, r->uri, (urlen - pglen)); + + /* Now open the directory and do ourselves a check... */ + if (apr_dir_open(&dir, good, r->pool) != APR_SUCCESS) { + /* Oops, not a directory... */ + return DECLINED; + } + + candidates = apr_array_make(r->pool, 2, sizeof(misspelled_file)); + + dotloc = ap_ind(bad, '.'); + if (dotloc == -1) { + dotloc = strlen(bad); + } + + while (apr_dir_read(&dirent, APR_FINFO_DIRENT, dir) == APR_SUCCESS) { + sp_reason q; + + /* + * If we end up with a "fixed" URL which is identical to the + * requested one, we must have found a broken symlink or some such. + * Do _not_ try to redirect this, it causes a loop! + */ + if (strcmp(bad, dirent.name) == 0) { + apr_dir_close(dir); + return OK; + } + + /* + * miscapitalization errors are checked first (like, e.g., lower case + * file, upper case request) + */ + else if (strcasecmp(bad, dirent.name) == 0) { + misspelled_file *sp_new; + + sp_new = (misspelled_file *) apr_array_push(candidates); + sp_new->name = apr_pstrdup(r->pool, dirent.name); + sp_new->quality = SP_MISCAPITALIZED; + } + + /* + * simple typing errors are checked next (like, e.g., + * missing/extra/transposed char) + */ + else if ((cfg->check_case_only == 0) + && ((q = spdist(bad, dirent.name)) != SP_VERYDIFFERENT)) { + misspelled_file *sp_new; + + sp_new = (misspelled_file *) apr_array_push(candidates); + sp_new->name = apr_pstrdup(r->pool, dirent.name); + sp_new->quality = q; + } + + /* + * The spdist() should have found the majority of the misspelled + * requests. It is of questionable use to continue looking for + * files with the same base name, but potentially of totally wrong + * type (index.html <-> index.db). + * + * If you're using MultiViews, and have a file named foobar.html, + * which you refer to as "foobar", and someone tried to access + * "Foobar", without CheckBasenameMatch, mod_speling won't find it, + * because it won't find anything matching that spelling. + * With the extension-munging, it would locate "foobar.html". + */ + else if (cfg->check_basename_match == 1) { + /* + * Okay... we didn't find anything. Now we take out the hard-core + * power tools. There are several cases here. Someone might have + * entered a wrong extension (.htm instead of .html or vice + * versa) or the document could be negotiated. At any rate, now + * we just compare stuff before the first dot. If it matches, we + * figure we got us a match. This can result in wrong things if + * there are files of different content types but the same prefix + * (e.g. foo.gif and foo.html) This code will pick the first one + * it finds. Better than a Not Found, though. + */ + int entloc = ap_ind(dirent.name, '.'); + if (entloc == -1) { + entloc = strlen(dirent.name); + } + + if ((dotloc == entloc) + && !strncasecmp(bad, dirent.name, dotloc)) { + misspelled_file *sp_new; + + sp_new = (misspelled_file *) apr_array_push(candidates); + sp_new->name = apr_pstrdup(r->pool, dirent.name); + sp_new->quality = SP_VERYDIFFERENT; + } + } + } + apr_dir_close(dir); + + if (candidates->nelts != 0) { + /* Wow... we found us a mispelling. Construct a fixed url */ + char *nuri; + const char *ref; + misspelled_file *variant = (misspelled_file *) candidates->elts; + int i; + + ref = apr_table_get(r->headers_in, "Referer"); + + qsort((void *) candidates->elts, candidates->nelts, + sizeof(misspelled_file), sort_by_quality); + + /* + * Conditions for immediate redirection: + * a) the first candidate was not found by stripping the suffix + * AND b) there exists only one candidate OR the best match is not + * ambiguous + * then return a redirection right away. + */ + if (variant[0].quality != SP_VERYDIFFERENT + && (candidates->nelts == 1 + || variant[0].quality != variant[1].quality)) { + + nuri = ap_escape_uri(r->pool, apr_pstrcat(r->pool, url, + variant[0].name, + r->path_info, NULL)); + if (r->parsed_uri.query) + nuri = apr_pstrcat(r->pool, nuri, "?", r->parsed_uri.query, NULL); + + apr_table_setn(r->headers_out, "Location", + ap_construct_url(r->pool, nuri, r)); + + ap_log_rerror(APLOG_MARK, APLOG_INFO, APR_SUCCESS, + r, + ref ? APLOGNO(03224) "Fixed spelling: %s to %s from %s" + : APLOGNO(03225) "Fixed spelling: %s to %s%s", + r->uri, nuri, + (ref ? ref : "")); + + return HTTP_MOVED_PERMANENTLY; + } + /* + * Otherwise, a "[300] Multiple Choices" list with the variants is + * returned. + */ + else { + apr_pool_t *p; + apr_table_t *notes; + apr_pool_t *sub_pool; + apr_array_header_t *t; + apr_array_header_t *v; + + + if (r->main == NULL) { + p = r->pool; + notes = r->notes; + } + else { + p = r->main->pool; + notes = r->main->notes; + } + + if (apr_pool_create(&sub_pool, p) != APR_SUCCESS) + return DECLINED; + apr_pool_tag(sub_pool, "speling_sub"); + + t = apr_array_make(sub_pool, candidates->nelts * 8 + 8, + sizeof(char *)); + v = apr_array_make(sub_pool, candidates->nelts * 5, + sizeof(char *)); + + /* Generate the response text. */ + + *(const char **)apr_array_push(t) = + "The document name you requested (<code>"; + *(const char **)apr_array_push(t) = ap_escape_html(sub_pool, r->uri); + *(const char **)apr_array_push(t) = + "</code>) could not be found on this server.\n" + "However, we found documents with names similar " + "to the one you requested.<p>" + "Available documents:\n<ul>\n"; + + for (i = 0; i < candidates->nelts; ++i) { + char *vuri; + const char *reason; + + reason = sp_reason_str[(int) (variant[i].quality)]; + /* The format isn't very neat... */ + vuri = apr_pstrcat(sub_pool, url, variant[i].name, r->path_info, + (r->parsed_uri.query != NULL) ? "?" : "", + (r->parsed_uri.query != NULL) + ? r->parsed_uri.query : "", + NULL); + *(const char **)apr_array_push(v) = "\""; + *(const char **)apr_array_push(v) = ap_escape_uri(sub_pool, vuri); + *(const char **)apr_array_push(v) = "\";\""; + *(const char **)apr_array_push(v) = reason; + *(const char **)apr_array_push(v) = "\""; + + *(const char **)apr_array_push(t) = "<li><a href=\""; + *(const char **)apr_array_push(t) = ap_escape_uri(sub_pool, vuri); + *(const char **)apr_array_push(t) = "\">"; + *(const char **)apr_array_push(t) = ap_escape_html(sub_pool, vuri); + *(const char **)apr_array_push(t) = "</a> ("; + *(const char **)apr_array_push(t) = reason; + *(const char **)apr_array_push(t) = ")\n"; + + /* + * when we have printed the "close matches" and there are + * more "distant matches" (matched by stripping the suffix), + * then we insert an additional separator text to suggest + * that the user LOOK CLOSELY whether these are really the + * files she wanted. + */ + if (i > 0 && i < candidates->nelts - 1 + && variant[i].quality != SP_VERYDIFFERENT + && variant[i + 1].quality == SP_VERYDIFFERENT) { + *(const char **)apr_array_push(t) = + "</ul>\nFurthermore, the following related " + "documents were found:\n<ul>\n"; + } + } + *(const char **)apr_array_push(t) = "</ul>\n"; + + /* If we know there was a referring page, add a note: */ + if (ref != NULL) { + *(const char **)apr_array_push(t) = + "Please consider informing the owner of the " + "referring page <tt>"; + *(const char **)apr_array_push(t) = ap_escape_html(sub_pool, ref); + *(const char **)apr_array_push(t) = + "</tt> about the broken link.\n"; + } + + + /* Pass our apr_table_t to http_protocol.c (see mod_negotiation): */ + apr_table_setn(notes, "variant-list", apr_array_pstrcat(p, t, 0)); + + apr_table_mergen(r->subprocess_env, "VARIANTS", + apr_array_pstrcat(p, v, ',')); + + apr_pool_destroy(sub_pool); + + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, + ref ? APLOGNO(03226) "Spelling fix: %s: %d candidates from %s" + : APLOGNO(03227) "Spelling fix: %s: %d candidates%s", + r->uri, candidates->nelts, + (ref ? ref : "")); + + return HTTP_MULTIPLE_CHOICES; + } + } + + return OK; +} + +static void register_hooks(apr_pool_t *p) +{ + ap_hook_fixups(check_speling,NULL,NULL,APR_HOOK_LAST); +} + +AP_DECLARE_MODULE(speling) = +{ + STANDARD20_MODULE_STUFF, + create_mconfig_for_directory, /* create per-dir config */ + NULL, /* merge per-dir config */ + create_mconfig_for_server, /* server config */ + NULL, /* merge server config */ + speling_cmds, /* command apr_table_t */ + register_hooks /* register hooks */ +}; diff --git a/modules/mappers/mod_speling.dep b/modules/mappers/mod_speling.dep new file mode 100644 index 0000000..6709aa8 --- /dev/null +++ b/modules/mappers/mod_speling.dep @@ -0,0 +1,51 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_speling.mak + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + + +.\mod_speling.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_request.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_filter.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + diff --git a/modules/mappers/mod_speling.dsp b/modules/mappers/mod_speling.dsp new file mode 100644 index 0000000..d8840b8 --- /dev/null +++ b/modules/mappers/mod_speling.dsp @@ -0,0 +1,111 @@ +# Microsoft Developer Studio Project File - Name="mod_speling" - 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_speling - 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_speling.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_speling.mak" CFG="mod_speling - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_speling - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_speling - 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_speling - 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_speling_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_speling.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_speling.so" /d LONG_NAME="speling_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_speling.so" /base:@..\..\os\win32\BaseAddr.ref,mod_speling.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_speling.so" /base:@..\..\os\win32\BaseAddr.ref,mod_speling.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_speling.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_speling - 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_speling_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_speling.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_speling.so" /d LONG_NAME="speling_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_speling.so" /base:@..\..\os\win32\BaseAddr.ref,mod_speling.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_speling.so" /base:@..\..\os\win32\BaseAddr.ref,mod_speling.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_speling.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_speling - Win32 Release" +# Name "mod_speling - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_speling.c +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/mappers/mod_speling.exp b/modules/mappers/mod_speling.exp new file mode 100644 index 0000000..a6ee8b5 --- /dev/null +++ b/modules/mappers/mod_speling.exp @@ -0,0 +1 @@ +speling_module diff --git a/modules/mappers/mod_speling.mak b/modules/mappers/mod_speling.mak new file mode 100644 index 0000000..b49233f --- /dev/null +++ b/modules/mappers/mod_speling.mak @@ -0,0 +1,353 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_speling.dsp +!IF "$(CFG)" == "" +CFG=mod_speling - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_speling - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_speling - Win32 Release" && "$(CFG)" != "mod_speling - 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_speling.mak" CFG="mod_speling - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_speling - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_speling - 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_speling - 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_speling.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_speling.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_speling.obj" + -@erase "$(INTDIR)\mod_speling.res" + -@erase "$(INTDIR)\mod_speling_src.idb" + -@erase "$(INTDIR)\mod_speling_src.pdb" + -@erase "$(OUTDIR)\mod_speling.exp" + -@erase "$(OUTDIR)\mod_speling.lib" + -@erase "$(OUTDIR)\mod_speling.pdb" + -@erase "$(OUTDIR)\mod_speling.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_speling_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_speling.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_speling.so" /d LONG_NAME="speling_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_speling.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_speling.pdb" /debug /out:"$(OUTDIR)\mod_speling.so" /implib:"$(OUTDIR)\mod_speling.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_speling.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_speling.obj" \ + "$(INTDIR)\mod_speling.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" + +"$(OUTDIR)\mod_speling.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_speling.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_speling.so" + if exist .\Release\mod_speling.so.manifest mt.exe -manifest .\Release\mod_speling.so.manifest -outputresource:.\Release\mod_speling.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_speling - 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_speling.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_speling.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_speling.obj" + -@erase "$(INTDIR)\mod_speling.res" + -@erase "$(INTDIR)\mod_speling_src.idb" + -@erase "$(INTDIR)\mod_speling_src.pdb" + -@erase "$(OUTDIR)\mod_speling.exp" + -@erase "$(OUTDIR)\mod_speling.lib" + -@erase "$(OUTDIR)\mod_speling.pdb" + -@erase "$(OUTDIR)\mod_speling.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_speling_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_speling.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_speling.so" /d LONG_NAME="speling_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_speling.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_speling.pdb" /debug /out:"$(OUTDIR)\mod_speling.so" /implib:"$(OUTDIR)\mod_speling.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_speling.so +LINK32_OBJS= \ + "$(INTDIR)\mod_speling.obj" \ + "$(INTDIR)\mod_speling.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" + +"$(OUTDIR)\mod_speling.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_speling.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_speling.so" + if exist .\Debug\mod_speling.so.manifest mt.exe -manifest .\Debug\mod_speling.so.manifest -outputresource:.\Debug\mod_speling.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_speling.dep") +!INCLUDE "mod_speling.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_speling.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_speling - Win32 Release" || "$(CFG)" == "mod_speling - Win32 Debug" + +!IF "$(CFG)" == "mod_speling - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\mappers" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_speling - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\mappers" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ENDIF + +!IF "$(CFG)" == "mod_speling - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\mappers" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_speling - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\mappers" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ENDIF + +!IF "$(CFG)" == "mod_speling - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\mappers" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_speling - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\mappers" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\mappers" + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_speling - Win32 Release" + + +"$(INTDIR)\mod_speling.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_speling.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_speling.so" /d LONG_NAME="speling_module for Apache" $(SOURCE) + + +!ELSEIF "$(CFG)" == "mod_speling - Win32 Debug" + + +"$(INTDIR)\mod_speling.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_speling.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_speling.so" /d LONG_NAME="speling_module for Apache" $(SOURCE) + + +!ENDIF + +SOURCE=.\mod_speling.c + +"$(INTDIR)\mod_speling.obj" : $(SOURCE) "$(INTDIR)" + + + +!ENDIF + diff --git a/modules/mappers/mod_userdir.c b/modules/mappers/mod_userdir.c new file mode 100644 index 0000000..1ec0e90 --- /dev/null +++ b/modules/mappers/mod_userdir.c @@ -0,0 +1,390 @@ +/* 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_userdir... implement the UserDir command. Broken away from the + * Alias stuff for a couple of good and not-so-good reasons: + * + * 1) It shows a real minimal working example of how to do something like + * this. + * 2) I know people who are actually interested in changing this *particular* + * aspect of server functionality without changing the rest of it. That's + * what this whole modular arrangement is supposed to be good at... + * + * Modified by Alexei Kosut to support the following constructs + * (server running at www.foo.com, request for /~bar/one/two.html) + * + * UserDir public_html -> ~bar/public_html/one/two.html + * UserDir /usr/web -> /usr/web/bar/one/two.html + * UserDir /home/ * /www -> /home/bar/www/one/two.html + * NOTE: theses ^ ^ space only added allow it to work in a comment, ignore + * UserDir http://x/users -> (302) http://x/users/bar/one/two.html + * UserDir http://x/ * /y -> (302) http://x/bar/y/one/two.html + * NOTE: here also ^ ^ + * + * In addition, you can use multiple entries, to specify alternate + * user directories (a la Directory Index). For example: + * + * UserDir public_html /usr/web http://www.xyz.com/users + * + * Modified by Ken Coar to provide for the following: + * + * UserDir disable[d] username ... + * UserDir enable[d] username ... + * + * If "disabled" has no other arguments, *all* ~<username> references are + * disabled, except those explicitly turned on with the "enabled" keyword. + */ + +#include "apr_strings.h" +#include "apr_user.h" + +#define APR_WANT_STRFUNC +#include "apr_want.h" + +#if APR_HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include "ap_config.h" +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" + +#if !defined(WIN32) && !defined(OS2) && !defined(NETWARE) +#define HAVE_UNIX_SUEXEC +#endif + +#ifdef HAVE_UNIX_SUEXEC +#include "unixd.h" /* Contains the suexec_identity hook used on Unix */ +#endif + + +/* + * The default directory in user's home dir + * In the default install, the module is disabled + */ +#ifndef DEFAULT_USER_DIR +#define DEFAULT_USER_DIR NULL +#endif + +#define O_DEFAULT 0 +#define O_ENABLE 1 +#define O_DISABLE 2 + +module AP_MODULE_DECLARE_DATA userdir_module; + +typedef struct { + int globally_disabled; + const char *userdir; + apr_table_t *enabled_users; + apr_table_t *disabled_users; +} userdir_config; + +/* + * Server config for this module: global disablement flag, a list of usernames + * ineligible for UserDir access, a list of those immune to global (but not + * explicit) disablement, and the replacement string for all others. + */ + +static void *create_userdir_config(apr_pool_t *p, server_rec *s) +{ + userdir_config *newcfg = apr_pcalloc(p, sizeof(*newcfg)); + + newcfg->globally_disabled = O_DEFAULT; + newcfg->userdir = DEFAULT_USER_DIR; + newcfg->enabled_users = apr_table_make(p, 4); + newcfg->disabled_users = apr_table_make(p, 4); + + return newcfg; +} + +static void *merge_userdir_config(apr_pool_t *p, void *basev, void *overridesv) +{ + userdir_config *cfg = apr_pcalloc(p, sizeof(userdir_config)); + userdir_config *base = basev, *overrides = overridesv; + + cfg->globally_disabled = (overrides->globally_disabled != O_DEFAULT) ? + overrides->globally_disabled : + base->globally_disabled; + cfg->userdir = (overrides->userdir != DEFAULT_USER_DIR) ? + overrides->userdir : base->userdir; + + /* not merged */ + cfg->enabled_users = overrides->enabled_users; + cfg->disabled_users = overrides->disabled_users; + + return cfg; +} + + +static const char *set_user_dir(cmd_parms *cmd, void *dummy, const char *arg) +{ + userdir_config *s_cfg = ap_get_module_config(cmd->server->module_config, + &userdir_module); + char *username; + const char *usernames = arg; + char *kw = ap_getword_conf(cmd->temp_pool, &usernames); + apr_table_t *usertable; + + /* Since we are a raw argument, it is possible for us to be called with + * zero arguments. So that we aren't ambiguous, flat out reject this. + */ + if (*kw == '\0') { + return "UserDir requires an argument."; + } + + /* + * Let's do the comparisons once. + */ + if ((!strcasecmp(kw, "disable")) || (!strcasecmp(kw, "disabled"))) { + /* + * If there are no usernames specified, this is a global disable - we + * need do no more at this point than record the fact. + */ + if (!*usernames) { + s_cfg->globally_disabled = O_DISABLE; + return NULL; + } + usertable = s_cfg->disabled_users; + } + else if ((!strcasecmp(kw, "enable")) || (!strcasecmp(kw, "enabled"))) { + if (!*usernames) { + s_cfg->globally_disabled = O_ENABLE; + return NULL; + } + usertable = s_cfg->enabled_users; + } + else { + /* + * If the first (only?) value isn't one of our keywords, just copy + * the string to the userdir string. + */ + s_cfg->userdir = arg; + return NULL; + } + /* + * Now we just take each word in turn from the command line and add it to + * the appropriate table. + */ + while (*usernames) { + username = ap_getword_conf(cmd->pool, &usernames); + apr_table_setn(usertable, username, "1"); + } + return NULL; +} + +static const command_rec userdir_cmds[] = { + AP_INIT_RAW_ARGS("UserDir", set_user_dir, NULL, RSRC_CONF, + "the public subdirectory in users' home directories, or " + "'disabled', or 'disabled username username...', or " + "'enabled username username...'"), + {NULL} +}; + +static int translate_userdir(request_rec *r) +{ + ap_conf_vector_t *server_conf; + const userdir_config *s_cfg; + const char *userdirs; + const char *user, *dname; + char *redirect; + apr_finfo_t statbuf; + + /* + * If the URI doesn't match our basic pattern, we've nothing to do with + * it. + */ + if (r->uri[0] != '/' || r->uri[1] != '~') { + return DECLINED; + } + server_conf = r->server->module_config; + s_cfg = ap_get_module_config(server_conf, &userdir_module); + userdirs = s_cfg->userdir; + if (userdirs == NULL) { + return DECLINED; + } + + dname = r->uri + 2; + user = ap_getword(r->pool, &dname, '/'); + + /* + * The 'dname' funny business involves backing it up to capture the '/' + * delimiting the "/~user" part from the rest of the URL, in case there + * was one (the case where there wasn't being just "GET /~user HTTP/1.0", + * for which we don't want to tack on a '/' onto the filename). + */ + + if (dname[-1] == '/') { + --dname; + } + + /* + * If there's no username, it's not for us. Ignore . and .. as well. + */ + if (user[0] == '\0' || + (user[1] == '.' && (user[2] == '\0' || + (user[2] == '.' && user[3] == '\0')))) { + return DECLINED; + } + /* + * Nor if there's an username but it's in the disabled list. + */ + if (apr_table_get(s_cfg->disabled_users, user) != NULL) { + return DECLINED; + } + /* + * If there's a global interdiction on UserDirs, check to see if this + * name is one of the Blessed. + */ + if (s_cfg->globally_disabled == O_DISABLE + && apr_table_get(s_cfg->enabled_users, user) == NULL) { + return DECLINED; + } + + /* + * Special cases all checked, onward to normal substitution processing. + */ + + while (*userdirs) { + const char *userdir = ap_getword_conf(r->pool, &userdirs); + char *filename = NULL, *prefix = NULL; + apr_status_t rv; + int is_absolute = ap_os_is_path_absolute(r->pool, userdir); + + if (ap_strchr_c(userdir, '*')) + prefix = ap_getword(r->pool, &userdir, '*'); + + if (userdir[0] == '\0' || is_absolute) { + if (prefix) { +#ifdef HAVE_DRIVE_LETTERS + /* + * Crummy hack. Need to figure out whether we have been + * redirected to a URL or to a file on some drive. Since I + * know of no protocols that are a single letter, ignore + * a : as the first or second character, and assume a file + * was specified + */ + if (strchr(prefix + 2, ':')) +#else + if (strchr(prefix, ':') && !is_absolute) +#endif /* HAVE_DRIVE_LETTERS */ + { + redirect = apr_pstrcat(r->pool, prefix, user, userdir, + dname, NULL); + apr_table_setn(r->headers_out, "Location", redirect); + return HTTP_MOVED_TEMPORARILY; + } + else + filename = apr_pstrcat(r->pool, prefix, user, userdir, + NULL); + } + else + filename = apr_pstrcat(r->pool, userdir, "/", user, NULL); + } + else if (prefix && ap_strchr_c(prefix, ':')) { + redirect = apr_pstrcat(r->pool, prefix, user, dname, NULL); + apr_table_setn(r->headers_out, "Location", redirect); + return HTTP_MOVED_TEMPORARILY; + } + else { +#if APR_HAS_USER + char *homedir; + + if (apr_uid_homepath_get(&homedir, user, r->pool) == APR_SUCCESS) { + filename = apr_pstrcat(r->pool, homedir, "/", userdir, NULL); + } +#else + return DECLINED; +#endif + } + + /* + * Now see if it exists, or we're at the last entry. If we are at the + * last entry, then use the filename generated (if there is one) + * anyway, in the hope that some handler might handle it. This can be + * used, for example, to run a CGI script for the user. + */ + if (filename && (!*userdirs + || ((rv = apr_stat(&statbuf, filename, APR_FINFO_MIN, + r->pool)) == APR_SUCCESS + || rv == APR_INCOMPLETE))) { + r->filename = apr_pstrcat(r->pool, filename, dname, NULL); + ap_set_context_info(r, apr_pstrmemdup(r->pool, r->uri, + dname - r->uri), + filename); + /* XXX: Does this walk us around FollowSymLink rules? + * When statbuf contains info on r->filename we can save a syscall + * by copying it to r->finfo + */ + if (*userdirs && dname[0] == 0) + r->finfo = statbuf; + + /* For use in the get_suexec_identity phase */ + apr_table_setn(r->notes, "mod_userdir_user", user); + + return OK; + } + } + + return DECLINED; +} + +#ifdef HAVE_UNIX_SUEXEC +static ap_unix_identity_t *get_suexec_id_doer(const request_rec *r) +{ + ap_unix_identity_t *ugid = NULL; +#if APR_HAS_USER + const char *username = apr_table_get(r->notes, "mod_userdir_user"); + + if (username == NULL) { + return NULL; + } + + if ((ugid = apr_palloc(r->pool, sizeof(*ugid))) == NULL) { + return NULL; + } + + if (apr_uid_get(&ugid->uid, &ugid->gid, username, r->pool) != APR_SUCCESS) { + return NULL; + } + + ugid->userdir = 1; +#endif + return ugid; +} +#endif /* HAVE_UNIX_SUEXEC */ + +static void register_hooks(apr_pool_t *p) +{ + static const char * const aszPre[]={ "mod_alias.c",NULL }; + static const char * const aszSucc[]={ "mod_vhost_alias.c",NULL }; + + ap_hook_translate_name(translate_userdir,aszPre,aszSucc,APR_HOOK_MIDDLE); +#ifdef HAVE_UNIX_SUEXEC + ap_hook_get_suexec_identity(get_suexec_id_doer,NULL,NULL,APR_HOOK_FIRST); +#endif +} + +AP_DECLARE_MODULE(userdir) = { + STANDARD20_MODULE_STUFF, + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + create_userdir_config, /* server config */ + merge_userdir_config, /* merge server config */ + userdir_cmds, /* command apr_table_t */ + register_hooks /* register hooks */ +}; diff --git a/modules/mappers/mod_userdir.dep b/modules/mappers/mod_userdir.dep new file mode 100644 index 0000000..2aff834 --- /dev/null +++ b/modules/mappers/mod_userdir.dep @@ -0,0 +1,46 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_userdir.mak + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + + +.\mod_userdir.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_request.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_filter.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + diff --git a/modules/mappers/mod_userdir.dsp b/modules/mappers/mod_userdir.dsp new file mode 100644 index 0000000..e40367f --- /dev/null +++ b/modules/mappers/mod_userdir.dsp @@ -0,0 +1,111 @@ +# Microsoft Developer Studio Project File - Name="mod_userdir" - 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_userdir - 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_userdir.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_userdir.mak" CFG="mod_userdir - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_userdir - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_userdir - 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_userdir - 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_userdir_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_userdir.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_userdir.so" /d LONG_NAME="userdir_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_userdir.so" /base:@..\..\os\win32\BaseAddr.ref,mod_userdir.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_userdir.so" /base:@..\..\os\win32\BaseAddr.ref,mod_userdir.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_userdir.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_userdir - 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_userdir_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_userdir.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_userdir.so" /d LONG_NAME="userdir_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_userdir.so" /base:@..\..\os\win32\BaseAddr.ref,mod_userdir.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_userdir.so" /base:@..\..\os\win32\BaseAddr.ref,mod_userdir.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_userdir.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_userdir - Win32 Release" +# Name "mod_userdir - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_userdir.c +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/mappers/mod_userdir.exp b/modules/mappers/mod_userdir.exp new file mode 100644 index 0000000..6b8b81d --- /dev/null +++ b/modules/mappers/mod_userdir.exp @@ -0,0 +1 @@ +userdir_module diff --git a/modules/mappers/mod_userdir.mak b/modules/mappers/mod_userdir.mak new file mode 100644 index 0000000..8087ce1 --- /dev/null +++ b/modules/mappers/mod_userdir.mak @@ -0,0 +1,353 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_userdir.dsp +!IF "$(CFG)" == "" +CFG=mod_userdir - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_userdir - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_userdir - Win32 Release" && "$(CFG)" != "mod_userdir - 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_userdir.mak" CFG="mod_userdir - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_userdir - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_userdir - 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_userdir - 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_userdir.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_userdir.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_userdir.obj" + -@erase "$(INTDIR)\mod_userdir.res" + -@erase "$(INTDIR)\mod_userdir_src.idb" + -@erase "$(INTDIR)\mod_userdir_src.pdb" + -@erase "$(OUTDIR)\mod_userdir.exp" + -@erase "$(OUTDIR)\mod_userdir.lib" + -@erase "$(OUTDIR)\mod_userdir.pdb" + -@erase "$(OUTDIR)\mod_userdir.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_userdir_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_userdir.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_userdir.so" /d LONG_NAME="userdir_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_userdir.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_userdir.pdb" /debug /out:"$(OUTDIR)\mod_userdir.so" /implib:"$(OUTDIR)\mod_userdir.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_userdir.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_userdir.obj" \ + "$(INTDIR)\mod_userdir.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" + +"$(OUTDIR)\mod_userdir.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_userdir.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_userdir.so" + if exist .\Release\mod_userdir.so.manifest mt.exe -manifest .\Release\mod_userdir.so.manifest -outputresource:.\Release\mod_userdir.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_userdir - 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_userdir.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_userdir.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_userdir.obj" + -@erase "$(INTDIR)\mod_userdir.res" + -@erase "$(INTDIR)\mod_userdir_src.idb" + -@erase "$(INTDIR)\mod_userdir_src.pdb" + -@erase "$(OUTDIR)\mod_userdir.exp" + -@erase "$(OUTDIR)\mod_userdir.lib" + -@erase "$(OUTDIR)\mod_userdir.pdb" + -@erase "$(OUTDIR)\mod_userdir.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_userdir_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_userdir.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_userdir.so" /d LONG_NAME="userdir_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_userdir.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_userdir.pdb" /debug /out:"$(OUTDIR)\mod_userdir.so" /implib:"$(OUTDIR)\mod_userdir.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_userdir.so +LINK32_OBJS= \ + "$(INTDIR)\mod_userdir.obj" \ + "$(INTDIR)\mod_userdir.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" + +"$(OUTDIR)\mod_userdir.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_userdir.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_userdir.so" + if exist .\Debug\mod_userdir.so.manifest mt.exe -manifest .\Debug\mod_userdir.so.manifest -outputresource:.\Debug\mod_userdir.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_userdir.dep") +!INCLUDE "mod_userdir.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_userdir.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_userdir - Win32 Release" || "$(CFG)" == "mod_userdir - Win32 Debug" + +!IF "$(CFG)" == "mod_userdir - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\mappers" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_userdir - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\mappers" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ENDIF + +!IF "$(CFG)" == "mod_userdir - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\mappers" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_userdir - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\mappers" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ENDIF + +!IF "$(CFG)" == "mod_userdir - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\mappers" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_userdir - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\mappers" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\mappers" + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_userdir - Win32 Release" + + +"$(INTDIR)\mod_userdir.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_userdir.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_userdir.so" /d LONG_NAME="userdir_module for Apache" $(SOURCE) + + +!ELSEIF "$(CFG)" == "mod_userdir - Win32 Debug" + + +"$(INTDIR)\mod_userdir.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_userdir.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_userdir.so" /d LONG_NAME="userdir_module for Apache" $(SOURCE) + + +!ENDIF + +SOURCE=.\mod_userdir.c + +"$(INTDIR)\mod_userdir.obj" : $(SOURCE) "$(INTDIR)" + + + +!ENDIF + diff --git a/modules/mappers/mod_vhost_alias.c b/modules/mappers/mod_vhost_alias.c new file mode 100644 index 0000000..b1e5bfb --- /dev/null +++ b/modules/mappers/mod_vhost_alias.c @@ -0,0 +1,457 @@ +/* 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_vhost_alias.c: support for dynamically configured mass virtual hosting + * + * Copyright (c) 1998-1999 Demon Internet Ltd. + * + * This software was submitted by Demon Internet to the Apache Software Foundation + * in May 1999. Future revisions and derivatives of this source code + * must acknowledge Demon Internet as the original contributor of + * this module. All other licensing and usage conditions are those + * of the Apache Software Foundation. + * + * Originally written by Tony Finch <fanf@demon.net> <dot@dotat.at>. + * + * Implementation ideas were taken from mod_alias.c. The overall + * concept is derived from the OVERRIDE_DOC_ROOT/OVERRIDE_CGIDIR + * patch to Apache 1.3b3 and a similar feature in Demon's thttpd, + * both written by James Grinter <jrg@blodwen.demon.co.uk>. + */ + +#include "apr.h" +#include "apr_strings.h" +#include "ap_hooks.h" +#include "apr_lib.h" + +#define APR_WANT_STRFUNC +#include "apr_want.h" + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_request.h" /* for ap_hook_translate_name */ + + +module AP_MODULE_DECLARE_DATA vhost_alias_module; + + +/* + * basic configuration things + * we abbreviate "mod_vhost_alias" to "mva" for shorter names + */ + +typedef enum { + VHOST_ALIAS_UNSET, VHOST_ALIAS_NONE, VHOST_ALIAS_NAME, VHOST_ALIAS_IP +} mva_mode_e; + +/* + * Per-server module config record. + */ +typedef struct mva_sconf_t { + const char *doc_root; + const char *cgi_root; + mva_mode_e doc_root_mode; + mva_mode_e cgi_root_mode; +} mva_sconf_t; + +static void *mva_create_server_config(apr_pool_t *p, server_rec *s) +{ + mva_sconf_t *conf; + + conf = (mva_sconf_t *) apr_pcalloc(p, sizeof(mva_sconf_t)); + conf->doc_root = NULL; + conf->cgi_root = NULL; + conf->doc_root_mode = VHOST_ALIAS_UNSET; + conf->cgi_root_mode = VHOST_ALIAS_UNSET; + return conf; +} + +static void *mva_merge_server_config(apr_pool_t *p, void *parentv, void *childv) +{ + mva_sconf_t *parent = (mva_sconf_t *) parentv; + mva_sconf_t *child = (mva_sconf_t *) childv; + mva_sconf_t *conf; + + conf = (mva_sconf_t *) apr_pcalloc(p, sizeof(*conf)); + if (child->doc_root_mode == VHOST_ALIAS_UNSET) { + conf->doc_root_mode = parent->doc_root_mode; + conf->doc_root = parent->doc_root; + } + else { + conf->doc_root_mode = child->doc_root_mode; + conf->doc_root = child->doc_root; + } + if (child->cgi_root_mode == VHOST_ALIAS_UNSET) { + conf->cgi_root_mode = parent->cgi_root_mode; + conf->cgi_root = parent->cgi_root; + } + else { + conf->cgi_root_mode = child->cgi_root_mode; + conf->cgi_root = child->cgi_root; + } + return conf; +} + + +/* + * These are just here to tell us what vhost_alias_set should do. + * We don't put anything into them; we just use the cell addresses. + */ +static int vhost_alias_set_doc_root_ip, + vhost_alias_set_cgi_root_ip, + vhost_alias_set_doc_root_name, + vhost_alias_set_cgi_root_name; + +static const char *vhost_alias_set(cmd_parms *cmd, void *dummy, const char *map) +{ + mva_sconf_t *conf; + mva_mode_e mode, *pmode; + const char **pmap; + const char *p; + + conf = (mva_sconf_t *) ap_get_module_config(cmd->server->module_config, + &vhost_alias_module); + /* there ought to be a better way of doing this */ + if (&vhost_alias_set_doc_root_ip == cmd->info) { + mode = VHOST_ALIAS_IP; + pmap = &conf->doc_root; + pmode = &conf->doc_root_mode; + } + else if (&vhost_alias_set_cgi_root_ip == cmd->info) { + mode = VHOST_ALIAS_IP; + pmap = &conf->cgi_root; + pmode = &conf->cgi_root_mode; + } + else if (&vhost_alias_set_doc_root_name == cmd->info) { + mode = VHOST_ALIAS_NAME; + pmap = &conf->doc_root; + pmode = &conf->doc_root_mode; + } + else if (&vhost_alias_set_cgi_root_name == cmd->info) { + mode = VHOST_ALIAS_NAME; + pmap = &conf->cgi_root; + pmode = &conf->cgi_root_mode; + } + else { + return "INTERNAL ERROR: unknown command info"; + } + + if (!ap_os_is_path_absolute(cmd->pool, map)) { + if (ap_cstr_casecmp(map, "none")) { + return "format string must be an absolute path, or 'none'"; + } + *pmap = NULL; + *pmode = VHOST_ALIAS_NONE; + return NULL; + } + + /* sanity check */ + p = map; + while (*p != '\0') { + if (*p++ != '%') { + continue; + } + /* we just found a '%' */ + if (*p == 'p' || *p == '%') { + ++p; + continue; + } + /* optional dash */ + if (*p == '-') { + ++p; + } + /* digit N */ + if (apr_isdigit(*p)) { + ++p; + } + else { + return "syntax error in format string"; + } + /* optional plus */ + if (*p == '+') { + ++p; + } + /* do we end here? */ + if (*p != '.') { + continue; + } + ++p; + /* optional dash */ + if (*p == '-') { + ++p; + } + /* digit M */ + if (apr_isdigit(*p)) { + ++p; + } + else { + return "syntax error in format string"; + } + /* optional plus */ + if (*p == '+') { + ++p; + } + } + *pmap = map; + *pmode = mode; + return NULL; +} + +static const command_rec mva_commands[] = +{ + AP_INIT_TAKE1("VirtualScriptAlias", vhost_alias_set, + &vhost_alias_set_cgi_root_name, RSRC_CONF, + "how to create a ScriptAlias based on the host"), + AP_INIT_TAKE1("VirtualDocumentRoot", vhost_alias_set, + &vhost_alias_set_doc_root_name, RSRC_CONF, + "how to create the DocumentRoot based on the host"), + AP_INIT_TAKE1("VirtualScriptAliasIP", vhost_alias_set, + &vhost_alias_set_cgi_root_ip, RSRC_CONF, + "how to create a ScriptAlias based on the host"), + AP_INIT_TAKE1("VirtualDocumentRootIP", vhost_alias_set, + &vhost_alias_set_doc_root_ip, RSRC_CONF, + "how to create the DocumentRoot based on the host"), + { NULL } +}; + + +/* + * This really wants to be a nested function + * but C is too feeble to support them. + */ +static APR_INLINE void vhost_alias_checkspace(request_rec *r, char *buf, + char **pdest, int size) +{ + /* XXX: what if size > HUGE_STRING_LEN? */ + if (*pdest + size > buf + HUGE_STRING_LEN) { + **pdest = '\0'; + if (r->filename) { + r->filename = apr_pstrcat(r->pool, r->filename, buf, NULL); + } + else { + r->filename = apr_pstrdup(r->pool, buf); + } + *pdest = buf; + } +} + +static void vhost_alias_interpolate(request_rec *r, const char *name, + const char *map, const char *uri) +{ + /* 0..9 9..0 */ + enum { MAXDOTS = 19 }; + const char *dots[MAXDOTS+1]; + int ndots; + + char buf[HUGE_STRING_LEN]; + char *dest; + const char *docroot; + + int N, M, Np, Mp, Nd, Md; + const char *start, *end; + + const char *p; + + ndots = 0; + dots[ndots++] = name-1; /* slightly naughty */ + for (p = name; *p; ++p) { + if (*p == '.' && ndots < MAXDOTS) { + dots[ndots++] = p; + } + } + dots[ndots] = p; + + r->filename = NULL; + + dest = buf; + while (*map) { + if (*map != '%') { + /* normal characters */ + vhost_alias_checkspace(r, buf, &dest, 1); + *dest++ = *map++; + continue; + } + /* we are in a format specifier */ + ++map; + /* %% -> % */ + if (*map == '%') { + ++map; + vhost_alias_checkspace(r, buf, &dest, 1); + *dest++ = '%'; + continue; + } + /* port number */ + if (*map == 'p') { + ++map; + /* no. of decimal digits in a short plus one */ + vhost_alias_checkspace(r, buf, &dest, 7); + dest += apr_snprintf(dest, 7, "%d", ap_get_server_port(r)); + continue; + } + /* deal with %-N+.-M+ -- syntax is already checked */ + M = 0; /* value */ + Np = Mp = 0; /* is there a plus? */ + Nd = Md = 0; /* is there a dash? */ + if (*map == '-') ++map, Nd = 1; + N = *map++ - '0'; + if (*map == '+') ++map, Np = 1; + if (*map == '.') { + ++map; + if (*map == '-') { + ++map, Md = 1; + } + M = *map++ - '0'; + if (*map == '+') { + ++map, Mp = 1; + } + } + /* note that N and M are one-based indices, not zero-based */ + start = dots[0]+1; /* ptr to the first character */ + end = dots[ndots]; /* ptr to the character after the last one */ + if (N != 0) { + if (N > ndots) { + start = "_"; + end = start+1; + } + else if (!Nd) { + start = dots[N-1]+1; + if (!Np) { + end = dots[N]; + } + } + else { + if (!Np) { + start = dots[ndots-N]+1; + } + end = dots[ndots-N+1]; + } + } + if (M != 0) { + if (M > end - start) { + start = "_"; + end = start+1; + } + else if (!Md) { + start = start+M-1; + if (!Mp) { + end = start+1; + } + } + else { + if (!Mp) { + start = end-M; + } + end = end-M+1; + } + } + vhost_alias_checkspace(r, buf, &dest, end - start); + for (p = start; p < end; ++p) { + *dest++ = apr_tolower(*p); + } + } + /* no double slashes */ + if (dest - buf > 0 && dest[-1] == '/') { + --dest; + } + *dest = '\0'; + + if (r->filename) + docroot = apr_pstrcat(r->pool, r->filename, buf, NULL); + else + docroot = apr_pstrdup(r->pool, buf); + r->filename = apr_pstrcat(r->pool, docroot, uri, NULL); + ap_set_context_info(r, NULL, docroot); + ap_set_document_root(r, docroot); +} + +static int mva_translate(request_rec *r) +{ + mva_sconf_t *conf; + const char *name, *map, *uri; + mva_mode_e mode; + const char *cgi; + + conf = (mva_sconf_t *) ap_get_module_config(r->server->module_config, + &vhost_alias_module); + cgi = NULL; + if (conf->cgi_root) { + cgi = strstr(r->uri, "cgi-bin/"); + if (cgi && (cgi != r->uri + strspn(r->uri, "/"))) { + cgi = NULL; + } + } + if (cgi) { + mode = conf->cgi_root_mode; + map = conf->cgi_root; + uri = cgi + strlen("cgi-bin"); + } + else if (r->uri[0] == '/') { + mode = conf->doc_root_mode; + map = conf->doc_root; + uri = r->uri; + } + else { + return DECLINED; + } + + if (mode == VHOST_ALIAS_NAME) { + name = ap_get_server_name(r); + } + else if (mode == VHOST_ALIAS_IP) { + name = r->connection->local_ip; + } + else { + return DECLINED; + } + + /* ### There is an optimization available here to determine the + * absolute portion of the path from the server config phase, + * through the first % segment, and note that portion of the path + * canonical_path buffer. + */ + r->canonical_filename = ""; + vhost_alias_interpolate(r, name, map, uri); + + if (cgi) { + /* see is_scriptaliased() in mod_cgi */ + r->handler = "cgi-script"; + apr_table_setn(r->notes, "alias-forced-type", r->handler); + ap_set_context_info(r, "/cgi-bin", NULL); + } + + return OK; +} + +static void register_hooks(apr_pool_t *p) +{ + static const char * const aszPre[]={ "mod_alias.c","mod_userdir.c",NULL }; + + ap_hook_translate_name(mva_translate, aszPre, NULL, APR_HOOK_MIDDLE); +} + +AP_DECLARE_MODULE(vhost_alias) = +{ + STANDARD20_MODULE_STUFF, + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + mva_create_server_config, /* server config */ + mva_merge_server_config, /* merge server configs */ + mva_commands, /* command apr_table_t */ + register_hooks /* register hooks */ +}; + diff --git a/modules/mappers/mod_vhost_alias.dep b/modules/mappers/mod_vhost_alias.dep new file mode 100644 index 0000000..7b520e3 --- /dev/null +++ b/modules/mappers/mod_vhost_alias.dep @@ -0,0 +1,50 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_vhost_alias.mak + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + + +.\mod_vhost_alias.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_request.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_filter.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + diff --git a/modules/mappers/mod_vhost_alias.dsp b/modules/mappers/mod_vhost_alias.dsp new file mode 100644 index 0000000..0273ab4 --- /dev/null +++ b/modules/mappers/mod_vhost_alias.dsp @@ -0,0 +1,111 @@ +# Microsoft Developer Studio Project File - Name="mod_vhost_alias" - 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_vhost_alias - 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_vhost_alias.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_vhost_alias.mak" CFG="mod_vhost_alias - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_vhost_alias - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_vhost_alias - 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_vhost_alias - 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_vhost_alias_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_vhost_alias.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_vhost_alias.so" /d LONG_NAME="vhost_alias_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_vhost_alias.so" /base:@..\..\os\win32\BaseAddr.ref,mod_vhost_alias.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_vhost_alias.so" /base:@..\..\os\win32\BaseAddr.ref,mod_vhost_alias.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_vhost_alias.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_vhost_alias - 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_vhost_alias_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_vhost_alias.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_vhost_alias.so" /d LONG_NAME="vhost_alias_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_vhost_alias.so" /base:@..\..\os\win32\BaseAddr.ref,mod_vhost_alias.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_vhost_alias.so" /base:@..\..\os\win32\BaseAddr.ref,mod_vhost_alias.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_vhost_alias.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_vhost_alias - Win32 Release" +# Name "mod_vhost_alias - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_vhost_alias.c +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/mappers/mod_vhost_alias.exp b/modules/mappers/mod_vhost_alias.exp new file mode 100644 index 0000000..b17666f --- /dev/null +++ b/modules/mappers/mod_vhost_alias.exp @@ -0,0 +1 @@ +vhost_alias_module diff --git a/modules/mappers/mod_vhost_alias.mak b/modules/mappers/mod_vhost_alias.mak new file mode 100644 index 0000000..62085ab --- /dev/null +++ b/modules/mappers/mod_vhost_alias.mak @@ -0,0 +1,353 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_vhost_alias.dsp +!IF "$(CFG)" == "" +CFG=mod_vhost_alias - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_vhost_alias - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_vhost_alias - Win32 Release" && "$(CFG)" != "mod_vhost_alias - 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_vhost_alias.mak" CFG="mod_vhost_alias - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_vhost_alias - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_vhost_alias - 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_vhost_alias - 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_vhost_alias.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_vhost_alias.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_vhost_alias.obj" + -@erase "$(INTDIR)\mod_vhost_alias.res" + -@erase "$(INTDIR)\mod_vhost_alias_src.idb" + -@erase "$(INTDIR)\mod_vhost_alias_src.pdb" + -@erase "$(OUTDIR)\mod_vhost_alias.exp" + -@erase "$(OUTDIR)\mod_vhost_alias.lib" + -@erase "$(OUTDIR)\mod_vhost_alias.pdb" + -@erase "$(OUTDIR)\mod_vhost_alias.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_vhost_alias_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_vhost_alias.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_vhost_alias.so" /d LONG_NAME="vhost_alias_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_vhost_alias.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_vhost_alias.pdb" /debug /out:"$(OUTDIR)\mod_vhost_alias.so" /implib:"$(OUTDIR)\mod_vhost_alias.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_vhost_alias.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_vhost_alias.obj" \ + "$(INTDIR)\mod_vhost_alias.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" + +"$(OUTDIR)\mod_vhost_alias.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_vhost_alias.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_vhost_alias.so" + if exist .\Release\mod_vhost_alias.so.manifest mt.exe -manifest .\Release\mod_vhost_alias.so.manifest -outputresource:.\Release\mod_vhost_alias.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_vhost_alias - 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_vhost_alias.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_vhost_alias.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_vhost_alias.obj" + -@erase "$(INTDIR)\mod_vhost_alias.res" + -@erase "$(INTDIR)\mod_vhost_alias_src.idb" + -@erase "$(INTDIR)\mod_vhost_alias_src.pdb" + -@erase "$(OUTDIR)\mod_vhost_alias.exp" + -@erase "$(OUTDIR)\mod_vhost_alias.lib" + -@erase "$(OUTDIR)\mod_vhost_alias.pdb" + -@erase "$(OUTDIR)\mod_vhost_alias.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_vhost_alias_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_vhost_alias.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_vhost_alias.so" /d LONG_NAME="vhost_alias_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_vhost_alias.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_vhost_alias.pdb" /debug /out:"$(OUTDIR)\mod_vhost_alias.so" /implib:"$(OUTDIR)\mod_vhost_alias.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_vhost_alias.so +LINK32_OBJS= \ + "$(INTDIR)\mod_vhost_alias.obj" \ + "$(INTDIR)\mod_vhost_alias.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" + +"$(OUTDIR)\mod_vhost_alias.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_vhost_alias.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_vhost_alias.so" + if exist .\Debug\mod_vhost_alias.so.manifest mt.exe -manifest .\Debug\mod_vhost_alias.so.manifest -outputresource:.\Debug\mod_vhost_alias.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_vhost_alias.dep") +!INCLUDE "mod_vhost_alias.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_vhost_alias.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_vhost_alias - Win32 Release" || "$(CFG)" == "mod_vhost_alias - Win32 Debug" + +!IF "$(CFG)" == "mod_vhost_alias - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\mappers" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_vhost_alias - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\mappers" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ENDIF + +!IF "$(CFG)" == "mod_vhost_alias - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\mappers" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_vhost_alias - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\mappers" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\mappers" + +!ENDIF + +!IF "$(CFG)" == "mod_vhost_alias - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\mappers" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\mappers" + +!ELSEIF "$(CFG)" == "mod_vhost_alias - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\mappers" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\mappers" + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_vhost_alias - Win32 Release" + + +"$(INTDIR)\mod_vhost_alias.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_vhost_alias.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_vhost_alias.so" /d LONG_NAME="vhost_alias_module for Apache" $(SOURCE) + + +!ELSEIF "$(CFG)" == "mod_vhost_alias - Win32 Debug" + + +"$(INTDIR)\mod_vhost_alias.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_vhost_alias.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_vhost_alias.so" /d LONG_NAME="vhost_alias_module for Apache" $(SOURCE) + + +!ENDIF + +SOURCE=.\mod_vhost_alias.c + +"$(INTDIR)\mod_vhost_alias.obj" : $(SOURCE) "$(INTDIR)" + + + +!ENDIF + |