diff options
Diffstat (limited to 'rubbos/app/httpd-2.0.64/modules/mappers')
81 files changed, 14626 insertions, 0 deletions
diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.deps b/rubbos/app/httpd-2.0.64/modules/mappers/.deps new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.deps diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.indent.pro b/rubbos/app/httpd-2.0.64/modules/mappers/.indent.pro new file mode 100644 index 00000000..a9fbe9f9 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/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/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_actions.a b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_actions.a Binary files differnew file mode 100644 index 00000000..86545023 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_actions.a diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_actions.la b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_actions.la new file mode 100644 index 00000000..4bc62514 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_actions.la @@ -0,0 +1,35 @@ +# mod_actions.la - a libtool library file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='' + +# Names of this library. +library_names='' + +# The name of the static archive. +old_library='mod_actions.a' + +# Libraries that this one depends upon. +dependency_libs=' -L/bottlenecks/rubbos/app/httpd-2.0.64/srclib/apr-util/xml/expat/lib' + +# Version information for mod_actions. +current= +age= +revision= + +# Is this an already installed library? +installed=no + +# Should we warn about portability when linking against -modules? +shouldnotlink=yes + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='' diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_actions.o b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_actions.o Binary files differnew file mode 100644 index 00000000..91f684c1 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_actions.o diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_alias.a b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_alias.a Binary files differnew file mode 100644 index 00000000..efd4192a --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_alias.a diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_alias.la b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_alias.la new file mode 100644 index 00000000..f008aff6 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_alias.la @@ -0,0 +1,35 @@ +# mod_alias.la - a libtool library file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='' + +# Names of this library. +library_names='' + +# The name of the static archive. +old_library='mod_alias.a' + +# Libraries that this one depends upon. +dependency_libs=' -L/bottlenecks/rubbos/app/httpd-2.0.64/srclib/apr-util/xml/expat/lib' + +# Version information for mod_alias. +current= +age= +revision= + +# Is this an already installed library? +installed=no + +# Should we warn about portability when linking against -modules? +shouldnotlink=yes + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='' diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_alias.o b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_alias.o Binary files differnew file mode 100644 index 00000000..178414cd --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_alias.o diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_dir.a b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_dir.a Binary files differnew file mode 100644 index 00000000..0fa02416 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_dir.a diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_dir.la b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_dir.la new file mode 100644 index 00000000..9fd727d8 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_dir.la @@ -0,0 +1,35 @@ +# mod_dir.la - a libtool library file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='' + +# Names of this library. +library_names='' + +# The name of the static archive. +old_library='mod_dir.a' + +# Libraries that this one depends upon. +dependency_libs=' -L/bottlenecks/rubbos/app/httpd-2.0.64/srclib/apr-util/xml/expat/lib' + +# Version information for mod_dir. +current= +age= +revision= + +# Is this an already installed library? +installed=no + +# Should we warn about portability when linking against -modules? +shouldnotlink=yes + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='' diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_dir.o b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_dir.o Binary files differnew file mode 100644 index 00000000..40dbfa1e --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_dir.o diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_imap.a b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_imap.a Binary files differnew file mode 100644 index 00000000..e30d4fd5 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_imap.a diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_imap.la b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_imap.la new file mode 100644 index 00000000..a51962d8 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_imap.la @@ -0,0 +1,35 @@ +# mod_imap.la - a libtool library file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='' + +# Names of this library. +library_names='' + +# The name of the static archive. +old_library='mod_imap.a' + +# Libraries that this one depends upon. +dependency_libs=' -L/bottlenecks/rubbos/app/httpd-2.0.64/srclib/apr-util/xml/expat/lib' + +# Version information for mod_imap. +current= +age= +revision= + +# Is this an already installed library? +installed=no + +# Should we warn about portability when linking against -modules? +shouldnotlink=yes + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='' diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_imap.o b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_imap.o Binary files differnew file mode 100644 index 00000000..e824bbdd --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_imap.o diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_negotiation.a b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_negotiation.a Binary files differnew file mode 100644 index 00000000..83353892 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_negotiation.a diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_negotiation.la b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_negotiation.la new file mode 100644 index 00000000..7b42e96c --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_negotiation.la @@ -0,0 +1,35 @@ +# mod_negotiation.la - a libtool library file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='' + +# Names of this library. +library_names='' + +# The name of the static archive. +old_library='mod_negotiation.a' + +# Libraries that this one depends upon. +dependency_libs=' -L/bottlenecks/rubbos/app/httpd-2.0.64/srclib/apr-util/xml/expat/lib' + +# Version information for mod_negotiation. +current= +age= +revision= + +# Is this an already installed library? +installed=no + +# Should we warn about portability when linking against -modules? +shouldnotlink=yes + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='' diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_negotiation.o b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_negotiation.o Binary files differnew file mode 100644 index 00000000..38f00426 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_negotiation.o diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_so.a b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_so.a Binary files differnew file mode 100644 index 00000000..ef52ec98 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_so.a diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_so.la b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_so.la new file mode 100644 index 00000000..1c9e8489 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_so.la @@ -0,0 +1,35 @@ +# mod_so.la - a libtool library file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='' + +# Names of this library. +library_names='' + +# The name of the static archive. +old_library='mod_so.a' + +# Libraries that this one depends upon. +dependency_libs=' -L/bottlenecks/rubbos/app/httpd-2.0.64/srclib/apr-util/xml/expat/lib' + +# Version information for mod_so. +current= +age= +revision= + +# Is this an already installed library? +installed=no + +# Should we warn about portability when linking against -modules? +shouldnotlink=yes + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='' diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_so.o b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_so.o Binary files differnew file mode 100644 index 00000000..71d442fa --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_so.o diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_userdir.a b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_userdir.a Binary files differnew file mode 100644 index 00000000..2f3d25ea --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_userdir.a diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_userdir.la b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_userdir.la new file mode 100644 index 00000000..2f7eeb0b --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_userdir.la @@ -0,0 +1,35 @@ +# mod_userdir.la - a libtool library file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='' + +# Names of this library. +library_names='' + +# The name of the static archive. +old_library='mod_userdir.a' + +# Libraries that this one depends upon. +dependency_libs=' -L/bottlenecks/rubbos/app/httpd-2.0.64/srclib/apr-util/xml/expat/lib' + +# Version information for mod_userdir. +current= +age= +revision= + +# Is this an already installed library? +installed=no + +# Should we warn about portability when linking against -modules? +shouldnotlink=yes + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='' diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_userdir.o b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_userdir.o Binary files differnew file mode 100644 index 00000000..677c39d1 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/.libs/mod_userdir.o diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/Makefile b/rubbos/app/httpd-2.0.64/modules/mappers/Makefile new file mode 100644 index 00000000..536b0888 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/Makefile @@ -0,0 +1,8 @@ +top_srcdir = /bottlenecks/rubbos/app/httpd-2.0.64 +top_builddir = /bottlenecks/rubbos/app/httpd-2.0.64 +srcdir = /bottlenecks/rubbos/app/httpd-2.0.64/modules/mappers +builddir = /bottlenecks/rubbos/app/httpd-2.0.64/modules/mappers +VPATH = /bottlenecks/rubbos/app/httpd-2.0.64/modules/mappers + +include $(top_srcdir)/build/special.mk + diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/Makefile.in b/rubbos/app/httpd-2.0.64/modules/mappers/Makefile.in new file mode 100644 index 00000000..167b343d --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/Makefile.in @@ -0,0 +1,3 @@ + +include $(top_srcdir)/build/special.mk + diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/NWGNUmakefile b/rubbos/app/httpd-2.0.64/modules/mappers/NWGNUmakefile new file mode 100644 index 00000000..62c9624d --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/NWGNUmakefile @@ -0,0 +1,247 @@ +# +# 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)/rewrite.nlm \ + $(OBJDIR)/speling.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 + copy $(OBJDIR)\*.nlm $(INSTALL)\Apache2\modules\*.* + +# +# Any specialized rules here +# + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(AP_WORK)\build\NWGNUtail.inc + + diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/NWGNUrewrite b/rubbos/app/httpd-2.0.64/modules/mappers/NWGNUrewrite new file mode 100644 index 00000000..960fca7c --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/NWGNUrewrite @@ -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 = 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 = _LibCPrelude + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = _LibCPostlude + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = AUTOUNLOAD, PSEUDOPREEMPTION + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/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 = \ + libcpre.o \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @$(APR)/aprlib.imp \ + @$(NWOS)/httpd.imp \ + @libc.imp \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + 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 $(AP_WORK)\build\NWGNUtail.inc + + diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/NWGNUspeling b/rubbos/app/httpd-2.0.64/modules/mappers/NWGNUspeling new file mode 100644 index 00000000..550def1a --- /dev/null +++ b/rubbos/app/httpd-2.0.64/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 = _LibCPrelude + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = _LibCPostlude + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = AUTOUNLOAD, PSEUDOPREEMPTION + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/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 = \ + libcpre.o \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @$(APR)/aprlib.imp \ + @$(NWOS)/httpd.imp \ + @libc.imp \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + 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 $(AP_WORK)\build\NWGNUtail.inc + + diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/NWGNUvhost b/rubbos/app/httpd-2.0.64/modules/mappers/NWGNUvhost new file mode 100644 index 00000000..8d68295a --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/NWGNUvhost @@ -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 = 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 = _LibCPrelude + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = _LibCPostlude + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = AUTOUNLOAD, PSEUDOPREEMPTION + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/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 = \ + libcpre.o \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @$(APR)/aprlib.imp \ + @$(NWOS)/httpd.imp \ + @libc.imp \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + 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 $(AP_WORK)\build\NWGNUtail.inc + + diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/config9.m4 b/rubbos/app/httpd-2.0.64/modules/mappers/config9.m4 new file mode 100644 index 00000000..adb66ea8 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/config9.m4 @@ -0,0 +1,57 @@ +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, , , yes) +APACHE_MODULE(dir, directory request handling, , , yes) +APACHE_MODULE(imap, server-side imagemaps, , , yes) +APACHE_MODULE(actions, Action triggering on requests, , , yes) +APACHE_MODULE(speling, correct common URL misspellings, , , most) +APACHE_MODULE(userdir, mapping of requests to user-specific directories, , , yes) +APACHE_MODULE(alias, mapping of requests to different filesystem parts, , , yes) + +APACHE_MODULE(rewrite, rule based URL manipulation, , , most) + + +APR_CHECK_APR_DEFINE(APR_HAS_DSO) + +case "x$enable_so" in + "xyes") + if test $ac_cv_define_APR_HAS_DSO = "no"; then + AC_MSG_ERROR([mod_so has been requested but cannot be built on your system]) + fi + ;; + "xshared") + AC_MSG_ERROR([mod_so can not be built as a shared DSO]) + ;; + "xno") + ;; + "x") + enable_so=$ac_cv_define_APR_HAS_DSO + ;; +esac + +dnl mod_so can only be built statically. If the user wants modules to +dnl be built as DSOs by default (eg. ./configure --enable-mods-shared=most) +dnl then we must override the default here. +if test "x$enable_so" = "xyes"; then + enable_so="static" +fi + +if test "$sharedobjs" = "yes"; then + if test $ac_cv_define_APR_HAS_DSO = "no"; then + AC_MSG_ERROR([shared objects have been requested but cannot be built since mod_so cannot be built]) + elif test $enable_so = "no"; then + AC_MSG_ERROR([shared objects have been requested but cannot be built since mod_so was disabled]) + fi +fi + +APACHE_MODULE(so, DSO capability, , , $enable_so) + +dnl ### why save the cache? +AC_CACHE_SAVE + +APACHE_MODPATH_FINISH diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_actions.c b/rubbos/app/httpd-2.0.64/modules/mappers/mod_actions.c new file mode 100644 index 00000000..f8af935a --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_actions.c @@ -0,0 +1,198 @@ +/* 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" +#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) +{ + action_dir_config *m = (action_dir_config *)m_v; + apr_table_setn(m->action_types, type, script); + 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; + + methnum = ap_method_number_of(method); + if (methnum == M_TRACE) + return "TRACE not allowed for Script"; + else if (methnum == M_INVALID) + return "Unknown method type for Script"; + else + m->scripted[methnum] = script; + + m->configured = 1; + return NULL; +} + +static const command_rec action_cmds[] = +{ + AP_INIT_TAKE2("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 ((t = apr_table_get(conf->action_types, + action ? action : ap_default_type(r)))) { + script = t; + if (r->finfo.filetype == 0) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "File does not exist: %s", r->filename); + return HTTP_NOT_FOUND; + } + } + + 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); +} + +module AP_MODULE_DECLARE_DATA actions_module = +{ + 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/rubbos/app/httpd-2.0.64/modules/mappers/mod_actions.dsp b/rubbos/app/httpd-2.0.64/modules/mappers/mod_actions.dsp new file mode 100644 index 00000000..f78be8f0 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_actions.dsp @@ -0,0 +1,128 @@ +# 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 /Zi /O2 /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 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:"Release/mod_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 + +!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 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_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 + +!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=.\mod_actions.rc +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\win32ver.awk + +!IF "$(CFG)" == "mod_actions - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_actions.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_actions.so "actions_module for Apache" ../../include/ap_release.h > .\mod_actions.rc + +# End Custom Build + +!ELSEIF "$(CFG)" == "mod_actions - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_actions.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_actions.so "actions_module for Apache" ../../include/ap_release.h > .\mod_actions.rc + +# End Custom Build + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_actions.exp b/rubbos/app/httpd-2.0.64/modules/mappers/mod_actions.exp new file mode 100644 index 00000000..8cc6cdb1 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_actions.exp @@ -0,0 +1 @@ +actions_module diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_actions.la b/rubbos/app/httpd-2.0.64/modules/mappers/mod_actions.la new file mode 100644 index 00000000..4bc62514 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_actions.la @@ -0,0 +1,35 @@ +# mod_actions.la - a libtool library file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='' + +# Names of this library. +library_names='' + +# The name of the static archive. +old_library='mod_actions.a' + +# Libraries that this one depends upon. +dependency_libs=' -L/bottlenecks/rubbos/app/httpd-2.0.64/srclib/apr-util/xml/expat/lib' + +# Version information for mod_actions. +current= +age= +revision= + +# Is this an already installed library? +installed=no + +# Should we warn about portability when linking against -modules? +shouldnotlink=yes + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='' diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_actions.lo b/rubbos/app/httpd-2.0.64/modules/mappers/mod_actions.lo new file mode 100644 index 00000000..a7d3e371 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_actions.lo @@ -0,0 +1,12 @@ +# mod_actions.lo - a libtool object file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object='.libs/mod_actions.o' + +# Name of the non-PIC object. +non_pic_object='mod_actions.o' + diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_actions.o b/rubbos/app/httpd-2.0.64/modules/mappers/mod_actions.o Binary files differnew file mode 100644 index 00000000..91f684c1 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_actions.o diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_alias.c b/rubbos/app/httpd-2.0.64/modules/mappers/mod_alias.c new file mode 100644 index 00000000..4e5b6ef0 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_alias.c @@ -0,0 +1,484 @@ +/* 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" + + +typedef struct { + const char *real; + const char *fake; + char *handler; + 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 { + apr_array_header_t *redirects; +} alias_dir_conf; + +module AP_MODULE_DECLARE_DATA alias_module; + +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); + 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 *f, const char *r, + 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; + + /* XX r can NOT be relative to DocumentRoot here... compat bug. */ + + if (use_regex) { + new->regexp = ap_pregcomp(cmd->pool, f, REG_EXTENDED); + if (new->regexp == NULL) + return "Regular expression could not be compiled."; + new->real = r; + } + 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 = r; + } + new->fake = f; + 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 *p = &entries[i]; + + if ( (!p->regexp && alias_matches(f, p->fake) > 0) + || (p->regexp && !ap_regexec(p->regexp, f, 0, NULL, 0))) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, + "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, + p->handler ? "Script" : "", + p->regexp ? "Match" : ""); + + break; /* one warning per alias should be sufficient */ + } + } + } + + return NULL; +} + +static const char *add_alias(cmd_parms *cmd, void *dummy, const char *f, + const char *r) +{ + return add_alias_internal(cmd, dummy, f, r, 0); +} + +static const char *add_alias_regex(cmd_parms *cmd, void *dummy, const char *f, + const char *r) +{ + return add_alias_internal(cmd, dummy, f, r, 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; + regex_t *r = NULL; + const char *f = arg2; + const char *url = arg3; + + if (!strcasecmp(arg1, "gone")) + status = HTTP_GONE; + 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); + else { + f = arg1; + url = arg2; + } + + if (use_regex) { + r = ap_pregcomp(cmd->pool, f, REG_EXTENDED); + if (r == NULL) + return "Regular expression could not be compiled."; + } + + if (ap_is_HTTP_REDIRECT(status)) { + if (!url) + return "URL to redirect to is missing"; + if (!use_regex && !ap_is_url(url)) + 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 = f; + new->real = url; + new->regexp = r; + 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 const command_rec alias_cmds[] = +{ + AP_INIT_TAKE2("Alias", add_alias, NULL, RSRC_CONF, + "a fakename and a realname"), + AP_INIT_TAKE2("ScriptAlias", add_alias, "cgi-script", RSRC_CONF, + "a fakename and a realname"), + AP_INIT_TAKE23("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 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_list(request_rec *r, apr_array_header_t *aliases, + int doesc, int *status) +{ + alias_entry *entries = (alias_entry *) aliases->elts; + regmatch_t regm[AP_MAX_REG_MATCH]; + char *found = NULL; + int i; + + for (i = 0; i < aliases->nelts; ++i) { + alias_entry *p = &entries[i]; + int l; + + if (p->regexp) { + if (!ap_regexec(p->regexp, r->uri, AP_MAX_REG_MATCH, regm, 0)) { + if (p->real) { + found = ap_pregsub(r->pool, p->real, r->uri, + AP_MAX_REG_MATCH, regm); + if (found && doesc) { + 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 { + /* need something non-null */ + found = apr_pstrdup(r->pool, ""); + } + } + } + else { + l = alias_matches(r->uri, p->fake); + + if (l > 0) { + if (doesc) { + char *escurl; + escurl = ap_os_escape_path(r->pool, r->uri + l, 1); + + found = apr_pstrcat(r->pool, p->real, escurl, NULL); + } + else + found = apr_pstrcat(r->pool, p->real, r->uri + l, NULL); + } + } + + if (found) { + if (p->handler) { /* Set handler, and leave a note for mod_cgi */ + r->handler = p->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 (!doesc) { + found = ap_server_root_relative(r->pool, found); + } + if (found) { + *status = p->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_alias_list(r, serverconf->redirects, 1, &status)) != NULL) { + if (ap_is_HTTP_REDIRECT(status)) { + /* include QUERY_STRING if any */ + if (r->args) { + ret = apr_pstrcat(r->pool, ret, "?", r->args, NULL); + } + apr_table_setn(r->headers_out, "Location", ret); + } + return status; + } + + if ((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_alias_list(r, dirconf->redirects, 1, &status)) != NULL) { + 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, + "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, + "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 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); +} + +module AP_MODULE_DECLARE_DATA alias_module = +{ + 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/rubbos/app/httpd-2.0.64/modules/mappers/mod_alias.dsp b/rubbos/app/httpd-2.0.64/modules/mappers/mod_alias.dsp new file mode 100644 index 00000000..b13219d1 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_alias.dsp @@ -0,0 +1,128 @@ +# 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 /Zi /O2 /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 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:"Release/mod_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 + +!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 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_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 + +!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=.\mod_alias.rc +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\win32ver.awk + +!IF "$(CFG)" == "mod_alias - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_alias.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_alias.so "alias_module for Apache" ../../include/ap_release.h > .\mod_alias.rc + +# End Custom Build + +!ELSEIF "$(CFG)" == "mod_alias - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_alias.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_alias.so "alias_module for Apache" ../../include/ap_release.h > .\mod_alias.rc + +# End Custom Build + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_alias.exp b/rubbos/app/httpd-2.0.64/modules/mappers/mod_alias.exp new file mode 100644 index 00000000..ac386ec3 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_alias.exp @@ -0,0 +1 @@ +alias_module diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_alias.la b/rubbos/app/httpd-2.0.64/modules/mappers/mod_alias.la new file mode 100644 index 00000000..f008aff6 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_alias.la @@ -0,0 +1,35 @@ +# mod_alias.la - a libtool library file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='' + +# Names of this library. +library_names='' + +# The name of the static archive. +old_library='mod_alias.a' + +# Libraries that this one depends upon. +dependency_libs=' -L/bottlenecks/rubbos/app/httpd-2.0.64/srclib/apr-util/xml/expat/lib' + +# Version information for mod_alias. +current= +age= +revision= + +# Is this an already installed library? +installed=no + +# Should we warn about portability when linking against -modules? +shouldnotlink=yes + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='' diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_alias.lo b/rubbos/app/httpd-2.0.64/modules/mappers/mod_alias.lo new file mode 100644 index 00000000..bf4ea8f5 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_alias.lo @@ -0,0 +1,12 @@ +# mod_alias.lo - a libtool object file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object='.libs/mod_alias.o' + +# Name of the non-PIC object. +non_pic_object='mod_alias.o' + diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_alias.o b/rubbos/app/httpd-2.0.64/modules/mappers/mod_alias.o Binary files differnew file mode 100644 index 00000000..178414cd --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_alias.o diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_dir.c b/rubbos/app/httpd-2.0.64/modules/mappers/mod_dir.c new file mode 100644 index 00000000..9191b7f0 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_dir.c @@ -0,0 +1,247 @@ +/* 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 "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" + +module AP_MODULE_DECLARE_DATA dir_module; + +typedef enum { + SLASH_OFF = 0, + SLASH_ON, + SLASH_UNSET +} slash_cfg; + +typedef struct dir_config_struct { + apr_array_header_t *index_names; + slash_cfg do_slash; +} 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; + + if (!d->index_names) { + d->index_names = apr_array_make(cmd->pool, 2, sizeof(char *)); + } + *(const char **)apr_array_push(d->index_names) = arg; + return NULL; +} + +static const char *configure_slash(cmd_parms *cmd, void *d_, int arg) +{ + dir_config_rec *d = d_; + + d->do_slash = arg ? SLASH_ON : SLASH_OFF; + return NULL; +} + +static const command_rec dir_cmds[] = +{ + AP_INIT_ITERATE("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"), + {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 = SLASH_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 == SLASH_UNSET) ? base->do_slash : add->do_slash; + return new; +} + +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; + + /* only handle requests against directories */ + if (r->finfo.filetype != APR_DIR) { + return DECLINED; + } + + /* 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; + } + + if (strcmp(r->handler, DIR_MAGIC_TYPE)) { + 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, NULL); + + /* XXX: (filetype == APR_REG) - we can't use a non-file index??? */ + 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; + } + + /* 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; + } + + /* nothing for us to do, pass on through */ + return DECLINED; +} + +static void register_hooks(apr_pool_t *p) +{ + ap_hook_fixups(fixup_dir,NULL,NULL,APR_HOOK_LAST); +} + +module AP_MODULE_DECLARE_DATA dir_module = { + 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/rubbos/app/httpd-2.0.64/modules/mappers/mod_dir.dsp b/rubbos/app/httpd-2.0.64/modules/mappers/mod_dir.dsp new file mode 100644 index 00000000..5863d90f --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_dir.dsp @@ -0,0 +1,128 @@ +# 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 /Zi /O2 /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 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:"Release/mod_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 + +!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 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_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 + +!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=.\mod_dir.rc +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\win32ver.awk + +!IF "$(CFG)" == "mod_dir - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_dir.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_dir.so "dir_module for Apache" ../../include/ap_release.h > .\mod_dir.rc + +# End Custom Build + +!ELSEIF "$(CFG)" == "mod_dir - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_dir.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_dir.so "dir_module for Apache" ../../include/ap_release.h > .\mod_dir.rc + +# End Custom Build + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_dir.exp b/rubbos/app/httpd-2.0.64/modules/mappers/mod_dir.exp new file mode 100644 index 00000000..5fbf7729 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_dir.exp @@ -0,0 +1 @@ +dir_module diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_dir.la b/rubbos/app/httpd-2.0.64/modules/mappers/mod_dir.la new file mode 100644 index 00000000..9fd727d8 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_dir.la @@ -0,0 +1,35 @@ +# mod_dir.la - a libtool library file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='' + +# Names of this library. +library_names='' + +# The name of the static archive. +old_library='mod_dir.a' + +# Libraries that this one depends upon. +dependency_libs=' -L/bottlenecks/rubbos/app/httpd-2.0.64/srclib/apr-util/xml/expat/lib' + +# Version information for mod_dir. +current= +age= +revision= + +# Is this an already installed library? +installed=no + +# Should we warn about portability when linking against -modules? +shouldnotlink=yes + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='' diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_dir.lo b/rubbos/app/httpd-2.0.64/modules/mappers/mod_dir.lo new file mode 100644 index 00000000..471329d6 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_dir.lo @@ -0,0 +1,12 @@ +# mod_dir.lo - a libtool object file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object='.libs/mod_dir.o' + +# Name of the non-PIC object. +non_pic_object='mod_dir.o' + diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_dir.o b/rubbos/app/httpd-2.0.64/modules/mappers/mod_dir.o Binary files differnew file mode 100644 index 00000000..40dbfa1e --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_dir.o diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_imap.c b/rubbos/app/httpd-2.0.64/modules/mappers/mod_imap.c new file mode 100644 index 00000000..f7745c65 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_imap.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" + +#ifdef SUNOS4 +double strtod(); /* SunOS needed this */ +#endif + +module AP_MODULE_DECLARE_DATA imap_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_pcalloc(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; pgon[numverts][X] != -1 && numverts < MAXVERTS; + 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 char *imap_url(request_rec *r, const char *base, const char *value) +{ +/* translates a value into a URL. */ + int 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 ap_escape_html(r->pool, 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, + "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, + "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, 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); + } + + return; +} + +static void menu_blank(request_rec *r, char *menu) +{ + if (!strcasecmp(menu, "formatted")) { + ap_rputs("\n", r); + } + if (!strcasecmp(menu, "semiformatted")) { + ap_rputs("<br />\n", r); + } + if (!strcasecmp(menu, "unformatted")) { + ap_rputs("\n", r); + } + return; +} + +static void menu_comment(request_rec *r, char *menu, char *comment) +{ + if (!strcasecmp(menu, "formatted")) { + ap_rputs("\n", r); /* print just a newline if 'formatted' */ + } + if (!strcasecmp(menu, "semiformatted") && *comment) { + ap_rvputs(r, comment, "\n", NULL); + } + if (!strcasecmp(menu, "unformatted") && *comment) { + ap_rvputs(r, comment, "\n", NULL); + } + return; /* comments are ignored in the + 'formatted' form */ +} + +static void menu_default(request_rec *r, char *menu, char *href, char *text) +{ + if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) { + return; /* don't print such lines, these aren't + really href's */ + } + if (!strcasecmp(menu, "formatted")) { + ap_rvputs(r, "<pre>(Default) <a href=\"", href, "\">", text, + "</a></pre>\n", NULL); + } + if (!strcasecmp(menu, "semiformatted")) { + ap_rvputs(r, "<pre>(Default) <a href=\"", href, "\">", text, + "</a></pre>\n", NULL); + } + if (!strcasecmp(menu, "unformatted")) { + ap_rvputs(r, "<a href=\"", href, "\">", text, "</a>", NULL); + } + return; +} + +static void menu_directive(request_rec *r, char *menu, char *href, char *text) +{ + if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) { + return; /* don't print such lines, as this isn't + really an href */ + } + if (!strcasecmp(menu, "formatted")) { + ap_rvputs(r, "<pre> <a href=\"", href, "\">", text, + "</a></pre>\n", NULL); + } + if (!strcasecmp(menu, "semiformatted")) { + ap_rvputs(r, "<pre> <a href=\"", href, "\">", text, + "</a></pre>\n", NULL); + } + if (!strcasecmp(menu, "unformatted")) { + ap_rvputs(r, "<a href=\"", href, "\">", text, "</a>", NULL); + } + return; +} + +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; + char *base; + char *redirect; + 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, &imap_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(*string_pos && 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, + "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); +} + +module AP_MODULE_DECLARE_DATA imap_module = +{ + 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/rubbos/app/httpd-2.0.64/modules/mappers/mod_imap.dsp b/rubbos/app/httpd-2.0.64/modules/mappers/mod_imap.dsp new file mode 100644 index 00000000..19e15bf3 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_imap.dsp @@ -0,0 +1,128 @@ +# Microsoft Developer Studio Project File - Name="mod_imap" - 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_imap - 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_imap.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_imap.mak" CFG="mod_imap - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_imap - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_imap - 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_imap - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /Zi /O2 /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_imap_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:"Release/mod_imap.so" /base:@..\..\os\win32\BaseAddr.ref,mod_imap.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Release/mod_imap.so" /base:@..\..\os\win32\BaseAddr.ref,mod_imap.so /opt:ref + +!ELSEIF "$(CFG)" == "mod_imap - 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_imap_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_imap.so" /base:@..\..\os\win32\BaseAddr.ref,mod_imap.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_imap.so" /base:@..\..\os\win32\BaseAddr.ref,mod_imap.so + +!ENDIF + +# Begin Target + +# Name "mod_imap - Win32 Release" +# Name "mod_imap - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_imap.c +# End Source File +# Begin Source File + +SOURCE=.\mod_imap.rc +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\win32ver.awk + +!IF "$(CFG)" == "mod_imap - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_imap.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_imap.so "imap_module for Apache" ../../include/ap_release.h > .\mod_imap.rc + +# End Custom Build + +!ELSEIF "$(CFG)" == "mod_imap - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_imap.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_imap.so "imap_module for Apache" ../../include/ap_release.h > .\mod_imap.rc + +# End Custom Build + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_imap.exp b/rubbos/app/httpd-2.0.64/modules/mappers/mod_imap.exp new file mode 100644 index 00000000..1e0e0b83 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_imap.exp @@ -0,0 +1 @@ +imap_module diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_imap.la b/rubbos/app/httpd-2.0.64/modules/mappers/mod_imap.la new file mode 100644 index 00000000..a51962d8 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_imap.la @@ -0,0 +1,35 @@ +# mod_imap.la - a libtool library file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='' + +# Names of this library. +library_names='' + +# The name of the static archive. +old_library='mod_imap.a' + +# Libraries that this one depends upon. +dependency_libs=' -L/bottlenecks/rubbos/app/httpd-2.0.64/srclib/apr-util/xml/expat/lib' + +# Version information for mod_imap. +current= +age= +revision= + +# Is this an already installed library? +installed=no + +# Should we warn about portability when linking against -modules? +shouldnotlink=yes + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='' diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_imap.lo b/rubbos/app/httpd-2.0.64/modules/mappers/mod_imap.lo new file mode 100644 index 00000000..4cfb46d6 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_imap.lo @@ -0,0 +1,12 @@ +# mod_imap.lo - a libtool object file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object='.libs/mod_imap.o' + +# Name of the non-PIC object. +non_pic_object='mod_imap.o' + diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_imap.o b/rubbos/app/httpd-2.0.64/modules/mappers/mod_imap.o Binary files differnew file mode 100644 index 00000000..e824bbdd --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_imap.o diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_negotiation.c b/rubbos/app/httpd-2.0.64/modules/mappers/mod_negotiation.c new file mode 100644 index 00000000..f8214cb4 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_negotiation.c @@ -0,0 +1,3096 @@ +/* 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 + +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. + */ + +/* + * 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 (*cp && (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 = (float)atof(cp); + } + else if (parm[0] == 'l' && !strcmp(&parm[1], "evel")) { + result->level = (float)atof(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, + "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; (*cp && 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 (!strncasecmp(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; + int bodylen; + int taglen; + apr_off_t pos; + + taglen = strlen(tag); + *len -= taglen; + + /* 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 + */ + if (apr_file_read(map, buffer, len) != APR_SUCCESS) { + return -1; + } + + /* put a copy of the tag *after* the data read from the file + * so that strstr() will find something with no reliance on + * terminating '\0' + */ + memcpy(buffer + *len, tag, taglen); + endbody = strstr(buffer, tag); + if (endbody == buffer + *len) { + 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, + "Syntax error in type map, no ':' in %s for header %s", + r->filename, header); + return NULL; + } + + do { + ++cp; + } while (*cp && apr_isspace(*cp)); + + if (!*cp) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "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, + "cannot access type map file: %s", rr->filename); + 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)) { + mime_info.bytes = apr_atoi64((char *)body); + 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, + "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_filename_of_pathname(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, + "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 (as opposed to something + * which we'll be slapping default_type on later). + */ + 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) + 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; + } + } + + /* + * ###: be warned, the _default_ content type is already + * picked up here! 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, + "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 (accept_type[0] == '*') { /* Anything matches star/star */ + if (avail->mime_stars < 1) { + avail->mime_stars = 1; + } + return 1; + } + else if ((accept_type[len - 1] == '*') && + !strncmp(accept_type, avail_type, len - 2)) { + 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 (!strncmp(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; + } + if (!neg->accept_langs) { + return; /* no accept-language header */ + } + 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; + alen = 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 = -1; + + /* 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; + } + } + } + } + return; +} + +/* 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, + "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, + "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) +{ + int j, max; + + /* fast exit */ + if ( !lang + || !variant->content_languages + || !(max = variant->content_languages->nelts)) { + return 0; + } + + for (j = 0; j < max; ++j) { + if (!strcmp(lang, + ((char **) (variant->content_languages->elts))[j])) { + return 1; + } + } + + return 0; +} + +static int best_match(negotiation_state *neg, var_rec **pbest) +{ + int j; + var_rec *best; + float bestq = 0.0f; + enum algorithm_results algorithm_result; + + var_rec *avail_recs = (var_rec *) neg->avail_vars->elts; + + const char *preferred_language = apr_table_get(neg->r->subprocess_env, + "prefer-language"); + + 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 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; + char *lenstr; + 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)) = 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. 22 bytes is enough for 2^64. + * + * 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) { + + lenstr = (char *) apr_palloc(r->pool, 22); + apr_snprintf(lenstr, 22, "%" APR_OFF_T_FMT, len); + *((const char **) apr_array_push(arr)) = " {length "; + *((const char **) apr_array_push(arr)) = lenstr; + *((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_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)) = filename; + *((const char **) apr_array_push(arr)) = "\">"; + *((const char **) apr_array_push(arr)) = 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, NULL); + 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 cachable. 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", + apr_pstrdup(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 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, + "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; + + 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); + */ + apr_table_setn(r->headers_out, "Accept-Ranges", "bytes"); + 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); + e = apr_bucket_file_create(map, best->body, + (apr_size_t)best->bytes, r->pool, + c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + e = apr_bucket_eos_create(c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + + return ap_pass_brigade(r->output_filters, bb); + } + + 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); + ap_internal_redirect(apr_pstrcat(r->pool, udir, best->file_name, + r->path_info, NULL), 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, NULL); + 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 amoung 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); +} + +module AP_MODULE_DECLARE_DATA negotiation_module = +{ + 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/rubbos/app/httpd-2.0.64/modules/mappers/mod_negotiation.dsp b/rubbos/app/httpd-2.0.64/modules/mappers/mod_negotiation.dsp new file mode 100644 index 00000000..fea69f66 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_negotiation.dsp @@ -0,0 +1,128 @@ +# 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 /Zi /O2 /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 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:"Release/mod_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 + +!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 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_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 + +!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=.\mod_negotiation.rc +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\win32ver.awk + +!IF "$(CFG)" == "mod_negotiation - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_negotiation.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_negotiation.so "negotiation_module for Apache" ../../include/ap_release.h > .\mod_negotiation.rc + +# End Custom Build + +!ELSEIF "$(CFG)" == "mod_negotiation - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_negotiation.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_negotiation.so "negotiation_module for Apache" ../../include/ap_release.h > .\mod_negotiation.rc + +# End Custom Build + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_negotiation.exp b/rubbos/app/httpd-2.0.64/modules/mappers/mod_negotiation.exp new file mode 100644 index 00000000..a7c18da1 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_negotiation.exp @@ -0,0 +1 @@ +negotiation_module diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_negotiation.la b/rubbos/app/httpd-2.0.64/modules/mappers/mod_negotiation.la new file mode 100644 index 00000000..7b42e96c --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_negotiation.la @@ -0,0 +1,35 @@ +# mod_negotiation.la - a libtool library file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='' + +# Names of this library. +library_names='' + +# The name of the static archive. +old_library='mod_negotiation.a' + +# Libraries that this one depends upon. +dependency_libs=' -L/bottlenecks/rubbos/app/httpd-2.0.64/srclib/apr-util/xml/expat/lib' + +# Version information for mod_negotiation. +current= +age= +revision= + +# Is this an already installed library? +installed=no + +# Should we warn about portability when linking against -modules? +shouldnotlink=yes + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='' diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_negotiation.lo b/rubbos/app/httpd-2.0.64/modules/mappers/mod_negotiation.lo new file mode 100644 index 00000000..c2852784 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_negotiation.lo @@ -0,0 +1,12 @@ +# mod_negotiation.lo - a libtool object file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object='.libs/mod_negotiation.o' + +# Name of the non-PIC object. +non_pic_object='mod_negotiation.o' + diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_negotiation.o b/rubbos/app/httpd-2.0.64/modules/mappers/mod_negotiation.o Binary files differnew file mode 100644 index 00000000..38f00426 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_negotiation.o diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_rewrite.c b/rubbos/app/httpd-2.0.64/modules/mappers/mod_rewrite.c new file mode 100644 index 00000000..dcbac539 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_rewrite.c @@ -0,0 +1,4670 @@ +/* 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" + +#define APR_WANT_STRFUNC +#define APR_WANT_IOVEC +#include "apr_want.h" + +#if APR_HAVE_UNISTD_H +#include <unistd.h> +#endif +#if APR_HAVE_SYS_TYPES_H +#include <sys/types.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 "mod_rewrite.h" + +/* mod_ssl.h is not safe for inclusion in 2.0, so duplicate the + * optional function declarations. */ +APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup, + (apr_pool_t *, server_rec *, + conn_rec *, request_rec *, + char *)); +APR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec *)); + +#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE) +#include "unixd.h" +#define MOD_REWRITE_SET_MUTEX_PERMS /* XXX Apache should define something */ +#endif + +/* +** +-------------------------------------------------------+ +** | | +** | static module configuration +** | | +** +-------------------------------------------------------+ +*/ + + +/* +** Our interface to the Apache server kernel: +** +** o Runtime logic of a request is as following: +** while(request or subrequest) +** foreach(stage #0...#9) +** foreach(module) (**) +** try to run hook +** +** o the order of modules at (**) is the inverted order as +** given in the "Configuration" file, i.e. the last module +** specified is the first one called for each hook! +** The core module is always the last! +** +** o there are two different types of result checking and +** continue processing: +** for hook #0,#1,#4,#5,#6,#8: +** hook run loop stops on first modules which gives +** back a result != DECLINED, i.e. it usually returns OK +** which says "OK, module has handled this _stage_" and for #1 +** this have not to mean "Ok, the filename is now valid". +** for hook #2,#3,#7,#9: +** all hooks are run, independend of result +** +** o at the last stage, the core module always +** - says "HTTP_BAD_REQUEST" if r->filename does not begin with "/" +** - prefix URL with document_root or replaced server_root +** with document_root and sets r->filename +** - always return a "OK" independed if the file really exists +** or not! +*/ + + /* the module (predeclaration) */ +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; + +static const char *lockname; +static apr_global_mutex_t *rewrite_mapr_lock_acquire = NULL; +static apr_global_mutex_t *rewrite_log_lock = NULL; + +/* Optional functions imported from mod_ssl when loaded: */ +static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *rewrite_ssl_lookup = NULL; +static APR_OPTIONAL_FN_TYPE(ssl_is_https) *rewrite_is_https = NULL; + +/* +** +-------------------------------------------------------+ +** | | +** | configuration directive handling +** | | +** +-------------------------------------------------------+ +*/ + +/* +** +** per-server configuration structure handling +** +*/ + +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->rewritelogfile = NULL; + a->rewritelogfp = NULL; + a->rewriteloglevel = 0; + a->rewritemaps = apr_array_make(p, 2, sizeof(rewritemap_entry)); + a->rewriteconds = apr_array_make(p, 2, sizeof(rewritecond_entry)); + a->rewriterules = apr_array_make(p, 2, sizeof(rewriterule_entry)); + a->server = s; + a->redirect_limit = 0; /* unset (use default) */ + + 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; + a->options = overrides->options; + a->server = overrides->server; + a->redirect_limit = overrides->redirect_limit + ? overrides->redirect_limit + : base->redirect_limit; + + if (a->options & OPTION_INHERIT) { + /* + * local directives override + * and anything else is inherited + */ + a->rewriteloglevel = overrides->rewriteloglevel != 0 + ? overrides->rewriteloglevel + : base->rewriteloglevel; + a->rewritelogfile = overrides->rewritelogfile != NULL + ? overrides->rewritelogfile + : base->rewritelogfile; + a->rewritelogfp = overrides->rewritelogfp != NULL + ? overrides->rewritelogfp + : base->rewritelogfp; + a->rewritemaps = apr_array_append(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 { + /* + * local directives override + * and anything else gets defaults + */ + a->rewriteloglevel = overrides->rewriteloglevel; + a->rewritelogfile = overrides->rewritelogfile; + a->rewritelogfp = overrides->rewritelogfp; + a->rewritemaps = overrides->rewritemaps; + a->rewriteconds = overrides->rewriteconds; + a->rewriterules = overrides->rewriterules; + } + + return (void *)a; +} + + +/* +** +** per-directory configuration structure handling +** +*/ + +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)); + a->redirect_limit = 0; /* unset (use server config) */ + + 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; + a->options = overrides->options; + a->directory = overrides->directory; + a->baseurl = overrides->baseurl; + a->redirect_limit = overrides->redirect_limit + ? overrides->redirect_limit + : base->redirect_limit; + + if (a->options & OPTION_INHERIT) { + a->rewriteconds = apr_array_append(p, overrides->rewriteconds, + base->rewriteconds); + a->rewriterules = apr_array_append(p, overrides->rewriterules, + base->rewriterules); + } + else { + a->rewriteconds = overrides->rewriteconds; + a->rewriterules = overrides->rewriterules; + } + + return (void *)a; +} + + +/* +** +** the configuration commands +** +*/ + +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); + + if (cmd->path == NULL) { /* is server command */ + sconf->state = (flag ? ENGINE_ENABLED : ENGINE_DISABLED); + } + else /* is per-directory command */ { + dconf->state = (flag ? ENGINE_ENABLED : ENGINE_DISABLED); + } + + return NULL; +} + +static const char *cmd_rewriteoptions(cmd_parms *cmd, + void *in_dconf, const char *option) +{ + int options = 0, limit = 0; + char *w; + + while (*option) { + w = ap_getword_conf(cmd->pool, &option); + + if (!strcasecmp(w, "inherit")) { + options |= OPTION_INHERIT; + } + else if (!strncasecmp(w, "MaxRedirects=", 13)) { + limit = atoi(&w[13]); + if (limit <= 0) { + return "RewriteOptions: MaxRedirects takes a number greater " + "than zero."; + } + } + else if (!strcasecmp(w, "MaxRedirects")) { /* be nice */ + return "RewriteOptions: MaxRedirects has the format MaxRedirects" + "=n."; + } + else { + return apr_pstrcat(cmd->pool, "RewriteOptions: unknown option '", + w, "'", NULL); + } + } + + /* put it into the appropriate config */ + if (cmd->path == NULL) { /* is server command */ + rewrite_server_conf *conf = + ap_get_module_config(cmd->server->module_config, + &rewrite_module); + + conf->options |= options; + conf->redirect_limit = limit; + } + else { /* is per-directory command */ + rewrite_perdir_conf *conf = in_dconf; + + conf->options |= options; + conf->redirect_limit = limit; + } + + return NULL; +} + +static const char *cmd_rewritelog(cmd_parms *cmd, void *dconf, const char *a1) +{ + rewrite_server_conf *sconf; + + sconf = ap_get_module_config(cmd->server->module_config, &rewrite_module); + + sconf->rewritelogfile = a1; + + return NULL; +} + +static const char *cmd_rewriteloglevel(cmd_parms *cmd, void *dconf, + const char *a1) +{ + rewrite_server_conf *sconf; + + sconf = ap_get_module_config(cmd->server->module_config, &rewrite_module); + + sconf->rewriteloglevel = atoi(a1); + + return NULL; +} + +static const char *cmd_rewritemap(cmd_parms *cmd, void *dconf, const char *a1, + const char *a2) +{ + rewrite_server_conf *sconf; + rewritemap_entry *newmap; + apr_finfo_t st; + + sconf = ap_get_module_config(cmd->server->module_config, &rewrite_module); + + newmap = apr_array_push(sconf->rewritemaps); + + newmap->name = a1; + newmap->func = NULL; + if (strncmp(a2, "txt:", 4) == 0) { + newmap->type = MAPTYPE_TXT; + newmap->datafile = a2+4; + newmap->checkfile = a2+4; + newmap->cachename = apr_psprintf(cmd->pool, "%pp:%s", + (void *)cmd->server, a1); + } + else if (strncmp(a2, "rnd:", 4) == 0) { + newmap->type = MAPTYPE_RND; + newmap->datafile = a2+4; + newmap->checkfile = a2+4; + newmap->cachename = apr_psprintf(cmd->pool, "%pp:%s", + (void *)cmd->server, a1); + } + else if (strncmp(a2, "dbm", 3) == 0) { + const char *ignored_fname; + int bad = 0; + apr_status_t rv; + + newmap->type = MAPTYPE_DBM; + newmap->cachename = apr_psprintf(cmd->pool, "%pp:%s", + (void *)cmd->server, a1); + + if (a2[3] == ':') { + newmap->dbmtype = "default"; + newmap->datafile = 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); + newmap->datafile = colon + 1; + } + else { + ++bad; + } + } + else { + ++bad; + } + + if (bad) { + return apr_pstrcat(cmd->pool, "RewriteMap: bad map:", + a2, NULL); + } + + rv = apr_dbm_get_usednames_ex(cmd->pool, newmap->dbmtype, + newmap->datafile, &newmap->checkfile, + &ignored_fname); + if (rv != APR_SUCCESS) { + return apr_pstrcat(cmd->pool, "RewriteMap: dbm type ", + newmap->dbmtype, " is invalid", NULL); + } + } + else if (strncmp(a2, "prg:", 4) == 0) { + newmap->type = MAPTYPE_PRG; + apr_tokenize_to_argv(a2 + 4, &newmap->argv, cmd->pool); + newmap->datafile = NULL; + newmap->checkfile = newmap->argv[0]; + newmap->cachename = NULL; + + } + else if (strncmp(a2, "int:", 4) == 0) { + newmap->type = MAPTYPE_INT; + newmap->datafile = NULL; + newmap->checkfile = NULL; + newmap->cachename = NULL; + newmap->func = (char *(*)(request_rec *,char *)) + apr_hash_get(mapfunc_hash, a2+4, strlen(a2+4)); + if ((sconf->state == ENGINE_ENABLED) && (newmap->func == NULL)) { + return apr_pstrcat(cmd->pool, "RewriteMap: internal map not found:", + a2+4, NULL); + } + } + else { + newmap->type = MAPTYPE_TXT; + newmap->datafile = a2; + newmap->checkfile = a2; + newmap->cachename = apr_psprintf(cmd->pool, "%pp:%s", + (void *)cmd->server, a1); + } + newmap->fpin = NULL; + newmap->fpout = NULL; + + if (newmap->checkfile && (sconf->state == ENGINE_ENABLED) + && (apr_stat(&st, newmap->checkfile, APR_FINFO_MIN, + cmd->pool) != APR_SUCCESS)) { + return apr_pstrcat(cmd->pool, + "RewriteMap: file for map ", newmap->name, + " not found:", newmap->checkfile, NULL); + } + + return NULL; +} + +static const char *cmd_rewritelock(cmd_parms *cmd, void *dconf, const char *a1) +{ + const char *error; + + if ((error = ap_check_cmd_context(cmd, GLOBAL_ONLY)) != NULL) + return error; + + /* fixup the path, especially for rewritelock_remove() */ + lockname = ap_server_root_relative(cmd->pool, a1); + + if (!lockname) { + return apr_pstrcat(cmd->pool, "Invalid RewriteLock path ", a1); + } + + 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; + + 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; + regex_t *regexp; + char *a1; + char *a2; + char *a3; + char *cp; + const char *err; + int rc; + + 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 */ + if (parseargline(str, &a1, &a2, &a3)) { + return apr_pstrcat(cmd->pool, "RewriteCond: bad argument line '", str, + "'", NULL); + } + + /* arg1: the input string */ + newcond->input = apr_pstrdup(cmd->pool, a1); + + /* arg3: optional flags field + (this have to be first parsed, because we need to + know if the regex should be compiled with ICASE!) */ + newcond->flags = CONDFLAG_NONE; + if (a3 != NULL) { + if ((err = cmd_rewritecond_parseflagfield(cmd->pool, newcond, + a3)) != NULL) { + return err; + } + } + + /* arg2: the pattern + try to compile the regexp to test if is ok */ + cp = a2; + if (cp[0] == '!') { + newcond->flags |= CONDFLAG_NOTMATCH; + cp++; + } + + /* now be careful: Under the POSIX regex library + we can compile the pattern for case insensitive matching, + under the old V8 library we have to do it self via a hack */ + if (newcond->flags & CONDFLAG_NOCASE) { + rc = ((regexp = ap_pregcomp(cmd->pool, cp, REG_EXTENDED|REG_ICASE)) + == NULL); + } + else { + rc = ((regexp = ap_pregcomp(cmd->pool, cp, REG_EXTENDED)) == NULL); + } + if (rc) { + return apr_pstrcat(cmd->pool, + "RewriteCond: cannot compile regular expression '", + a2, "'", NULL); + } + + newcond->pattern = apr_pstrdup(cmd->pool, cp); + newcond->regexp = regexp; + + return NULL; +} + +static const char *cmd_rewritecond_parseflagfield(apr_pool_t *p, + rewritecond_entry *cfg, + char *str) +{ + char *cp; + char *cp1; + char *cp2; + char *cp3; + char *key; + char *val; + const char *err; + + if (str[0] != '[' || str[strlen(str)-1] != ']') { + return "RewriteCond: bad flag delimiters"; + } + + cp = str+1; + str[strlen(str)-1] = ','; /* for simpler parsing */ + for ( ; *cp != '\0'; ) { + /* skip whitespaces */ + for ( ; (*cp == ' ' || *cp == '\t') && *cp != '\0'; cp++) + ; + if (*cp == '\0') { + break; + } + cp1 = cp; + if ((cp2 = strchr(cp, ',')) != NULL) { + cp = cp2+1; + for ( ; (*(cp2-1) == ' ' || *(cp2-1) == '\t'); cp2--) + ; + *cp2 = '\0'; + if ((cp3 = strchr(cp1, '=')) != NULL) { + *cp3 = '\0'; + key = cp1; + val = cp3+1; + } + else { + key = cp1; + val = ""; + } + if ((err = cmd_rewritecond_setflag(p, cfg, key, val)) != NULL) { + return err; + } + } + else { + break; + } + } + + return NULL; +} + +static const char *cmd_rewritecond_setflag(apr_pool_t *p, + rewritecond_entry *cfg, + char *key, char *val) +{ + 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 { + return apr_pstrcat(p, "RewriteCond: 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; + regex_t *regexp; + char *a1; + char *a2; + char *a3; + char *cp; + const char *err; + int mode; + + 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, &a3)) { + return apr_pstrcat(cmd->pool, "RewriteRule: bad argument line '", str, + "'", NULL); + } + + /* arg3: optional flags field */ + newrule->forced_mimetype = NULL; + newrule->forced_responsecode = HTTP_MOVED_TEMPORARILY; + newrule->flags = RULEFLAG_NONE; + newrule->env[0] = NULL; + newrule->cookie[0] = NULL; + newrule->skip = 0; + if (a3 != NULL) { + if ((err = cmd_rewriterule_parseflagfield(cmd->pool, newrule, + a3)) != NULL) { + return err; + } + } + + /* arg1: the pattern + * try to compile the regexp to test if is ok + */ + cp = a1; + if (cp[0] == '!') { + newrule->flags |= RULEFLAG_NOTMATCH; + cp++; + } + mode = REG_EXTENDED; + if (newrule->flags & RULEFLAG_NOCASE) { + mode |= REG_ICASE; + } + if ((regexp = ap_pregcomp(cmd->pool, cp, mode)) == NULL) { + return apr_pstrcat(cmd->pool, + "RewriteRule: cannot compile regular expression '", + a1, "'", NULL); + } + newrule->pattern = apr_pstrdup(cmd->pool, cp); + newrule->regexp = regexp; + + /* arg2: the output string + * replace the $<N> by \<n> which is needed by the currently + * used Regular Expression library + * + * TODO: Is this still required for PCRE? If not, does it *work* with PCRE? + */ + newrule->output = apr_pstrdup(cmd->pool, a2); + + /* 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; +} + +static const char *cmd_rewriterule_parseflagfield(apr_pool_t *p, + rewriterule_entry *cfg, + char *str) +{ + char *cp; + char *cp1; + char *cp2; + char *cp3; + char *key; + char *val; + const char *err; + + if (str[0] != '[' || str[strlen(str)-1] != ']') { + return "RewriteRule: bad flag delimiters"; + } + + cp = str+1; + str[strlen(str)-1] = ','; /* for simpler parsing */ + for ( ; *cp != '\0'; ) { + /* skip whitespaces */ + for ( ; (*cp == ' ' || *cp == '\t') && *cp != '\0'; cp++) + ; + if (*cp == '\0') { + break; + } + cp1 = cp; + if ((cp2 = strchr(cp, ',')) != NULL) { + cp = cp2+1; + for ( ; (*(cp2-1) == ' ' || *(cp2-1) == '\t'); cp2--) + ; + *cp2 = '\0'; + if ((cp3 = strchr(cp1, '=')) != NULL) { + *cp3 = '\0'; + key = cp1; + val = cp3+1; + } + else { + key = cp1; + val = ""; + } + if ((err = cmd_rewriterule_setflag(p, cfg, key, val)) != NULL) { + return err; + } + } + else { + break; + } + } + + return NULL; +} + +static const char *cmd_rewriterule_setflag(apr_pool_t *p, + rewriterule_entry *cfg, + char *key, char *val) +{ + int status = 0; + int i; + + if ( strcasecmp(key, "redirect") == 0 + || strcasecmp(key, "R") == 0 ) { + cfg->flags |= RULEFLAG_FORCEREDIRECT; + if (strlen(val) > 0) { + 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 (!ap_is_HTTP_REDIRECT(status)) { + return "RewriteRule: invalid HTTP response code " + "for flag 'R'"; + } + cfg->forced_responsecode = status; + } + } + else if ( strcasecmp(key, "noescape") == 0 + || strcasecmp(key, "NE") == 0 ) { + cfg->flags |= RULEFLAG_NOESCAPE; + } + else if ( strcasecmp(key, "last") == 0 + || strcasecmp(key, "L") == 0 ) { + cfg->flags |= RULEFLAG_LASTRULE; + } + else if ( strcasecmp(key, "next") == 0 + || strcasecmp(key, "N") == 0 ) { + cfg->flags |= RULEFLAG_NEWROUND; + } + else if ( strcasecmp(key, "chain") == 0 + || strcasecmp(key, "C") == 0 ) { + cfg->flags |= RULEFLAG_CHAIN; + } + else if ( strcasecmp(key, "type") == 0 + || strcasecmp(key, "T") == 0 ) { + cfg->forced_mimetype = apr_pstrdup(p, val); + ap_str_tolower(cfg->forced_mimetype); + } + else if ( strcasecmp(key, "env") == 0 + || strcasecmp(key, "E") == 0 ) { + for (i = 0; (cfg->env[i] != NULL) && (i < MAX_ENV_FLAGS); i++) + ; + if (i < MAX_ENV_FLAGS) { + cfg->env[i] = apr_pstrdup(p, val); + cfg->env[i+1] = NULL; + } + else { + return "RewriteRule: too many environment flags 'E'"; + } + } + else if ( strcasecmp(key, "cookie") == 0 || strcasecmp(key, "CO") == 0) { + for (i = 0; (cfg->cookie[i] != NULL) && (i < MAX_COOKIE_FLAGS); i++) + ; + if (i < MAX_COOKIE_FLAGS) { + cfg->cookie[i] = apr_pstrdup(p, val); + cfg->cookie[i+1] = NULL; + } + else { + return "RewriteRule: too many cookie flags 'CO'"; + } + } + else if ( strcasecmp(key, "nosubreq") == 0 + || strcasecmp(key, "NS") == 0 ) { + cfg->flags |= RULEFLAG_IGNOREONSUBREQ; + } + else if ( strcasecmp(key, "proxy") == 0 + || strcasecmp(key, "P") == 0 ) { + cfg->flags |= RULEFLAG_PROXY; + } + else if ( strcasecmp(key, "passthrough") == 0 + || strcasecmp(key, "PT") == 0 ) { + cfg->flags |= RULEFLAG_PASSTHROUGH; + } + else if ( strcasecmp(key, "skip") == 0 + || strcasecmp(key, "S") == 0 ) { + cfg->skip = atoi(val); + } + else if ( strcasecmp(key, "forbidden") == 0 + || strcasecmp(key, "F") == 0 ) { + cfg->flags |= RULEFLAG_FORBIDDEN; + } + else if ( strcasecmp(key, "gone") == 0 + || strcasecmp(key, "G") == 0 ) { + cfg->flags |= RULEFLAG_GONE; + } + else if ( strcasecmp(key, "qsappend") == 0 + || strcasecmp(key, "QSA") == 0 ) { + cfg->flags |= RULEFLAG_QSAPPEND; + } + else if ( strcasecmp(key, "nocase") == 0 + || strcasecmp(key, "NC") == 0 ) { + cfg->flags |= RULEFLAG_NOCASE; + } + else { + return apr_pstrcat(p, "RewriteRule: unknown flag '", key, "'", NULL); + } + return NULL; +} + + +/* +** +** Global Module Initialization +** +*/ + +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; + + /* register int: rewritemap handlers */ + mapfunc_hash = apr_hash_make(pconf); + 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); + } + 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; + void *data; + int first_time = 0; + const char *userdata_key = "rewrite_init_module"; + + apr_pool_userdata_get(&data, userdata_key, s->process->pool); + if (!data) { + first_time = 1; + apr_pool_userdata_set((const void *)1, userdata_key, + apr_pool_cleanup_null, s->process->pool); + } + + /* check if proxy module is available */ + proxy_available = (ap_find_linked_module("mod_proxy.c") != NULL); + + /* create the rewriting lockfiles in the parent */ + if ((rv = apr_global_mutex_create(&rewrite_log_lock, NULL, + APR_LOCK_DEFAULT, p)) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, + "mod_rewrite: could not create rewrite_log_lock"); + return HTTP_INTERNAL_SERVER_ERROR; + } + +#ifdef MOD_REWRITE_SET_MUTEX_PERMS + rv = unixd_set_global_mutex_perms(rewrite_log_lock); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, + "mod_rewrite: Could not set permissions on " + "rewrite_log_lock; check User and Group directives"); + return HTTP_INTERNAL_SERVER_ERROR; + } +#endif + + 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); + + /* step through the servers and + * - open each rewriting logfile + * - open the RewriteMap prg:xxx programs + */ + for (; s; s = s->next) { + if (!open_rewritelog(s, p)) { + return HTTP_INTERNAL_SERVER_ERROR; + } + + if (!first_time) { + if (run_rewritemap_programs(s, p) != APR_SUCCESS) { + return HTTP_INTERNAL_SERVER_ERROR; + } + } + } + + rewrite_ssl_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup); + rewrite_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https); + + return OK; +} + + +/* +** +** Per-Child Module Initialization +** [called after a child process is spawned] +** +*/ + +static void init_child(apr_pool_t *p, server_rec *s) +{ + apr_status_t rv; + + if (lockname != NULL && *(lockname) != '\0') { + rv = apr_global_mutex_child_init(&rewrite_mapr_lock_acquire, + lockname, p); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, + "mod_rewrite: could not init rewrite_mapr_lock_acquire" + " in child"); + } + } + + rv = apr_global_mutex_child_init(&rewrite_log_lock, NULL, p); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, + "mod_rewrite: could not init rewrite log lock in child"); + } + + /* create the lookup cache */ + cachep = init_cache(p); +} + + +/* +** +-------------------------------------------------------+ +** | | +** | runtime hooks +** | | +** +-------------------------------------------------------+ +*/ + +/* +** +** URI-to-filename hook +** +** [used for the rewriting engine triggered by +** the per-server 'RewriteRule' directives] +** +*/ + +static int hook_uri2file(request_rec *r) +{ + rewrite_server_conf *conf; + const char *saved_rulestatus; + const char *var; + const char *thisserver; + char *thisport; + const char *thisurl; + char buf[512]; + char docroot[512]; + const char *ccp; + unsigned int port; + int rulestatus; + int n; + int l; + + /* + * retrieve the config structures + */ + conf = ap_get_module_config(r->server->module_config, &rewrite_module); + + /* + * only do something under runtime if the engine is really enabled, + * else return immediately! + */ + if (conf->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; + } + + /* + * 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_pstrcat(r->pool, "REDIRECT_", ENVVAR_SCRIPT_URL, NULL); + var = apr_table_get(r->subprocess_env, var); + 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(r); + port = ap_get_server_port(r); + if (ap_is_default_port(port, r)) { + thisport = ""; + } + else { + apr_snprintf(buf, sizeof(buf), ":%u", port); + thisport = buf; + } + thisurl = apr_table_get(r->subprocess_env, ENVVAR_SCRIPT_URL); + + /* set the variable */ + var = apr_pstrcat(r->pool, ap_http_method(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, "init rewrite engine with requested uri %s", + r->filename); + } + else { + rewritelog(r, 2, "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_set(r->notes,"mod_rewrite_rewritten", + apr_psprintf(r->pool,"%d",rulestatus)); + } + else { + rewritelog(r, 2, + "uri already rewritten. Status %s, Uri %s, r->filename %s", + saved_rulestatus, r->uri, r->filename); + rulestatus = atoi(saved_rulestatus); + } + + if (rulestatus) { + unsigned skip; + + if (strlen(r->filename) > 6 && + strncmp(r->filename, "proxy:", 6) == 0) { + /* 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, + "attempt to make remote request from mod_rewrite " + "without proxy enabled: %s", r->filename); + return HTTP_FORBIDDEN; + } + + /* 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->uri == r->unparsed_uri) { + /* 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, "go-ahead with proxy request %s [OK]", + r->filename); + return OK; + } + else if ((skip = is_absolute_uri(r->filename)) > 0) { + /* it was finally rewritten to a remote URL */ + + if (rulestatus != ACTION_NOESCAPE) { + rewritelog(r, 1, "escaping %s for redirect", r->filename); + r->filename = escape_absolute_uri(r->pool, r->filename, skip); + } + + /* append the QUERY_STRING part */ + if (r->args) { + r->filename = apr_pstrcat(r->pool, r->filename, "?", + (rulestatus == ACTION_NOESCAPE) + ? r->args + : ap_escape_uri(r->pool, r->args), + NULL); + } + + /* 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, "redirect to %s [REDIRECT/%d]", r->filename, n); + return n; + } + else if (strlen(r->filename) > 10 && + strncmp(r->filename, "forbidden:", 10) == 0) { + /* This URLs is forced to be forbidden for the requester */ + return HTTP_FORBIDDEN; + } + else if (strlen(r->filename) > 5 && + strncmp(r->filename, "gone:", 5) == 0) { + /* This URLs is forced to be gone */ + return HTTP_GONE; + } + else if (strlen(r->filename) > 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 */ + + /* expand "/~user" prefix */ +#if APR_HAS_USER + r->filename = expand_tildepaths(r, r->filename); +#endif + rewritelog(r, 2, "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 have + * to emulate 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! + */ + n = prefix_stat(r->filename, r->pool); + if (n == 0) { + if ((ccp = ap_document_root(r)) != NULL) { + l = apr_cpystrn(docroot, ccp, sizeof(docroot)) - docroot; + + /* always NOT have a trailing slash */ + if (docroot[l-1] == '/') { + docroot[l-1] = '\0'; + } + if (r->server->path + && !strncmp(r->filename, r->server->path, + r->server->pathlen)) { + r->filename = apr_pstrcat(r->pool, docroot, + (r->filename + + r->server->pathlen), NULL); + } + else { + r->filename = apr_pstrcat(r->pool, docroot, + r->filename, NULL); + } + rewritelog(r, 2, "prefixed with document_root to %s", + r->filename); + } + } + + rewritelog(r, 1, "go-ahead with %s [OK]", r->filename); + return OK; + } + } + else { + rewritelog(r, 1, "pass through %s", r->filename); + return DECLINED; + } +} + + +/* +** +** MIME-type hook +** +** [used to support the forced-MIME-type feature] +** +*/ + +static int hook_mimetype(request_rec *r) +{ + const char *t; + + /* now check if we have to force a MIME-type */ + t = apr_table_get(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR); + if (t == NULL) { + return DECLINED; + } + else { + rewritelog(r, 1, "force filename %s to have MIME-type '%s'", + r->filename, t); + ap_set_content_type(r, t); + return OK; + } +} + + +/* +** +** Fixup hook +** +** [used for the rewriting engine triggered by +** the per-directory 'RewriteRule' directives] +** +*/ + +static int hook_fixup(request_rec *r) +{ + rewrite_perdir_conf *dconf; + char *cp; + char *cp2; + const char *ccp; + char *prefix; + apr_size_t l; + int rulestatus; + int n; + char *ofilename; + int is_proxyreq; + + 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; + } + + /* we shouldn't do anything in subrequests */ + if (r->main != NULL) { + 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, since they may lead to undefined behaviour. + */ + if (is_proxyreq) { + l = strlen(dconf->directory) - 1; + if (r->filename && strlen(r->filename) == l && + (dconf->directory)[l] == '/' && + !strncmp(r->filename, dconf->directory, l)) { + 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; + } + + /* + * Do the Options check after engine check, so + * the user is able to explicitely 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, + "Options FollowSymLinks or SymLinksIfOwnerMatch is off " + "which implies that RewriteRule directive is forbidden: " + "%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 inital one. + * also, we'll restore original r->filename if we decline this + * request + */ + ofilename = r->filename; + + if (r->filename == NULL) { + r->filename = apr_pstrdup(r->pool, r->uri); + rewritelog(r, 2, "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; + + if (strlen(r->filename) > 6 && + strncmp(r->filename, "proxy:", 6) == 0) { + /* 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) { + 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, "[per-dir %s] go-ahead with proxy request " + "%s [OK]", dconf->directory, r->filename); + return OK; + } + else if ((skip = is_absolute_uri(r->filename)) > 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; + + if ((cp = ap_strchr(cp, '/')) != NULL && *(++cp)) { + rewritelog(r, 2, + "[per-dir %s] trying to replace " + "prefix %s with %s", + dconf->directory, 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, "[per-dir %s] escaping %s for redirect", + dconf->directory, r->filename); + r->filename = escape_absolute_uri(r->pool, r->filename, skip); + } + + /* append the QUERY_STRING part */ + if (r->args) { + r->filename = apr_pstrcat(r->pool, r->filename, "?", + (rulestatus == ACTION_NOESCAPE) + ? r->args + : ap_escape_uri(r->pool, r->args), + NULL); + } + + /* 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, "[per-dir %s] redirect to %s [REDIRECT/%d]", + dconf->directory, r->filename, n); + return n; + } + else if (strlen(r->filename) > 10 && + strncmp(r->filename, "forbidden:", 10) == 0) { + /* This URL is forced to be forbidden for the requester */ + return HTTP_FORBIDDEN; + } + else if (strlen(r->filename) > 5 && + strncmp(r->filename, "gone:", 5) == 0) { + /* This URL is forced to be gone */ + return HTTP_GONE; + } + else { + /* 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 (strlen(r->filename) > 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, "[per-dir %s] initial URL equal rewritten " + "URL: %s [IGNORING REWRITE]", + dconf->directory, r->filename); + return OK; + } + + /* 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, + "[per-dir %s] trying to replace prefix %s with %s", + dconf->directory, 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) { + prefix = apr_pstrdup(r->pool, ccp); + /* always NOT have a trailing slash */ + l = strlen(prefix); + if (prefix[l-1] == '/') { + prefix[l-1] = '\0'; + l--; + } + if (strncmp(r->filename, prefix, l) == 0) { + rewritelog(r, 2, + "[per-dir %s] strip document_root " + "prefix: %s -> %s", + dconf->directory, r->filename, + r->filename+l); + r->filename = apr_pstrdup(r->pool, r->filename+l); + } + } + } + + /* now initiate the internal redirect */ + rewritelog(r, 1, "[per-dir %s] internal redirect with %s " + "[INTERNAL REDIRECT]", dconf->directory, r->filename); + r->filename = apr_pstrcat(r->pool, "redirect:", r->filename, NULL); + r->handler = "redirect-handler"; + return OK; + } + } + else { + rewritelog(r, 1, "[per-dir %s] pass through %s", + dconf->directory, r->filename); + r->filename = ofilename; + return DECLINED; + } +} + + +/* +** +** Content-Handlers +** +** [used for redirect support] +** +*/ + +static int handler_redirect(request_rec *r) +{ + if (strcmp(r->handler, "redirect-handler")) { + return DECLINED; + } + + /* just make sure that we are really meant! */ + if (strncmp(r->filename, "redirect:", 9) != 0) { + return DECLINED; + } + + if (is_redirect_limit_exceeded(r)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "mod_rewrite: maximum number of internal redirects " + "reached. Assuming configuration error. Use " + "'RewriteOptions MaxRedirects' to increase the limit " + "if neccessary."); + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* 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; +} + +/* + * check whether redirect limit is reached + */ +static int is_redirect_limit_exceeded(request_rec *r) +{ + request_rec *top = r; + rewrite_request_conf *reqc; + rewrite_perdir_conf *dconf; + + /* we store it in the top request */ + while (top->main) { + top = top->main; + } + while (top->prev) { + top = top->prev; + } + + /* fetch our config */ + reqc = (rewrite_request_conf *) ap_get_module_config(top->request_config, + &rewrite_module); + + /* no config there? create one. */ + if (!reqc) { + rewrite_server_conf *sconf; + + reqc = apr_palloc(top->pool, sizeof(rewrite_request_conf)); + sconf = ap_get_module_config(r->server->module_config, &rewrite_module); + + reqc->redirects = 0; + reqc->redirect_limit = sconf->redirect_limit + ? sconf->redirect_limit + : REWRITE_REDIRECT_LIMIT; + + /* associate it with this request */ + ap_set_module_config(top->request_config, &rewrite_module, reqc); + } + + /* allow to change the limit during redirects. */ + dconf = (rewrite_perdir_conf *)ap_get_module_config(r->per_dir_config, + &rewrite_module); + + /* 0 == unset; take server conf ... */ + if (dconf->redirect_limit) { + reqc->redirect_limit = dconf->redirect_limit; + } + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + "mod_rewrite's internal redirect status: %d/%d.", + reqc->redirects, reqc->redirect_limit); + + /* and now give the caller a hint */ + return (reqc->redirects++ >= reqc->redirect_limit); +} + + +/* +** +-------------------------------------------------------+ +** | | +** | the rewriting engine +** | | +** +-------------------------------------------------------+ +*/ + +/* + * 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; + + /* + * 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_PROXY || + p->flags & RULEFLAG_FORCEREDIRECT )) { + continue; + } + + /* + * Apply the current rule. + */ + rc = apply_rewrite_rule(r, p, perdir); + if (rc) { + /* + * 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, "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; + } + + /* + * Rule has the "forbidden" flag set which means that + * we stop processing and indicate this to the caller. + */ + if (p->flags & RULEFLAG_FORBIDDEN) { + rewritelog(r, 2, "forcing '%s' to be forbidden", r->filename); + r->filename = apr_pstrcat(r->pool, "forbidden:", + r->filename, NULL); + changed = ACTION_NORMAL; + break; + } + + /* + * Rule has the "gone" flag set which means that + * we stop processing and indicate this to the caller. + */ + if (p->flags & RULEFLAG_GONE) { + rewritelog(r, 2, "forcing '%s' to be gone", r->filename); + r->filename = apr_pstrcat(r->pool, "gone:", r->filename, NULL); + changed = ACTION_NORMAL; + break; + } + + /* + * Stop processing also on proxy pass-through and + * last-rule and new-round flags. + */ + if (p->flags & RULEFLAG_PROXY) { + break; + } + if (p->flags & RULEFLAG_LASTRULE) { + break; + } + + /* + * On "new-round" flag we just start from the top of + * the rewriting ruleset again. + */ + if (p->flags & RULEFLAG_NEWROUND) { + 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++; + p = &entries[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; +} + +/* + * Apply a single(!) rewrite rule + */ +static int apply_rewrite_rule(request_rec *r, rewriterule_entry *p, + char *perdir) +{ + char *uri; + char *output; + const char *vary; + char newuri[MAX_STRING_LEN]; + regex_t *regexp; + regmatch_t regmatch[AP_MAX_REG_MATCH]; + backrefinfo *briRR = NULL; + backrefinfo *briRC = NULL; + int failed; + apr_array_header_t *rewriteconds; + rewritecond_entry *conds; + rewritecond_entry *c; + int i; + int rc; + int is_proxyreq = 0; + + /* + * Initialisation + */ + uri = r->filename; + regexp = p->regexp; + output = p->output; + + /* + * Add (perhaps splitted away) PATH_INFO postfix to URL to + * make sure we really match against the complete URL. + */ + if (perdir != NULL && r->path_info != NULL && r->path_info[0] != '\0') { + rewritelog(r, 3, "[per-dir %s] add path info postfix: %s -> %s%s", + perdir, uri, uri, r->path_info); + uri = apr_pstrcat(r->pool, uri, r->path_info, NULL); + } + + /* + * On per-directory context (.htaccess) strip the location + * prefix from the URL to make sure patterns apply only to + * the local part. Additionally indicate this special + * threatment in the logfile. + */ + if (perdir) { + /* + * Proxy request? + */ + is_proxyreq = ( r->proxyreq && r->filename + && !strncmp(r->filename, "proxy:", 6)); + + if ( !is_proxyreq && strlen(uri) >= strlen(perdir) + && strncmp(uri, perdir, strlen(perdir)) == 0) { + rewritelog(r, 3, "[per-dir %s] strip per-dir prefix: %s -> %s", + perdir, uri, uri+strlen(perdir)); + uri = uri+strlen(perdir); + } + } + + /* + * Try to match the URI against the RewriteRule pattern + * and exit immeddiately if it didn't apply. + */ + if (perdir == NULL) { + rewritelog(r, 3, "applying pattern '%s' to uri '%s'", + p->pattern, uri); + } + else { + rewritelog(r, 3, "[per-dir %s] applying pattern '%s' to uri '%s'", + perdir, p->pattern, uri); + } + rc = (ap_regexec(regexp, uri, AP_MAX_REG_MATCH, regmatch, 0) == 0); + if (! (( rc && !(p->flags & RULEFLAG_NOTMATCH)) || + (!rc && (p->flags & RULEFLAG_NOTMATCH)) ) ) { + return 0; + } + + /* + * Else create the RewriteRule `regsubinfo' structure which + * holds the substitution information. + */ + briRR = (backrefinfo *)apr_palloc(r->pool, sizeof(backrefinfo)); + if (!rc && (p->flags & RULEFLAG_NOTMATCH)) { + /* empty info on negative patterns */ + briRR->source = ""; + briRR->nsub = 0; + } + else { + briRR->source = apr_pstrdup(r->pool, uri); + briRR->nsub = regexp->re_nsub; + memcpy((void *)(briRR->regmatch), (void *)(regmatch), + sizeof(regmatch)); + } + + /* + * Initiallally create the RewriteCond backrefinfo with + * empty backrefinfo, i.e. not subst parts + * (this one is adjusted inside apply_rewrite_cond() later!!) + */ + briRC = (backrefinfo *)apr_pcalloc(r->pool, sizeof(backrefinfo)); + briRC->source = ""; + briRC->nsub = 0; + + /* + * 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 unnessesary checks which + * would slow down the rewriting engine!! + */ + rewriteconds = p->rewriteconds; + conds = (rewritecond_entry *)rewriteconds->elts; + failed = 0; + for (i = 0; i < rewriteconds->nelts; i++) { + c = &conds[i]; + rc = apply_rewrite_cond(r, c, perdir, briRR, briRC); + if (c->flags & CONDFLAG_ORNEXT) { + /* + * The "OR" case + */ + if (rc == 0) { + /* One condition is false, but another can be + * still true, so we have to continue... + */ + apr_table_unset(r->notes, VARY_KEY_THIS); + continue; + } + else { + /* One true condition is enough in "or" case, so + * skip the other conditions which are "ornext" + * chained + */ + while ( i < rewriteconds->nelts + && c->flags & CONDFLAG_ORNEXT) { + i++; + c = &conds[i]; + } + continue; + } + } + else { + /* + * The "AND" case, i.e. no "or" flag, + * so a single failure means total failure. + */ + if (rc == 0) { + failed = 1; + break; + } + } + vary = apr_table_get(r->notes, VARY_KEY_THIS); + if (vary != NULL) { + apr_table_merge(r->notes, VARY_KEY, vary); + apr_table_unset(r->notes, VARY_KEY_THIS); + } + } + /* if any condition fails the complete rule fails */ + if (failed) { + apr_table_unset(r->notes, VARY_KEY); + apr_table_unset(r->notes, VARY_KEY_THIS); + return 0; + } + + /* + * 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 ((vary = apr_table_get(r->notes, VARY_KEY)) != NULL) { + apr_table_merge(r->headers_out, "Vary", vary); + apr_table_unset(r->notes, VARY_KEY); + } + + /* + * If this is a pure matching rule (`RewriteRule <pat> -') + * we stop processing and return immediately. The only thing + * we have not to forget are the environment variables and + * cookies: + * (`RewriteRule <pat> - [E=...,CO=...]') + */ + if (strcmp(output, "-") == 0) { + do_expand_env(r, p->env, briRR, briRC); + do_expand_cookie(r, p->cookie, briRR, briRC); + if (p->forced_mimetype != NULL) { + if (perdir == NULL) { + /* In the per-server context we can force the MIME-type + * the correct way by notifying our MIME-type hook handler + * to do the job when the MIME-type API stage is reached. + */ + rewritelog(r, 2, "remember %s to have MIME-type '%s'", + r->filename, p->forced_mimetype); + apr_table_setn(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR, + p->forced_mimetype); + } + else { + /* In per-directory context we operate in the Fixup API hook + * which is after the MIME-type hook, so our MIME-type handler + * has no chance to set r->content_type. And because we are + * in the situation where no substitution takes place no + * sub-request will happen (which could solve the + * restriction). As a workaround we do it ourself now + * immediately although this is not strictly API-conforming. + * But it's the only chance we have... + */ + rewritelog(r, 1, "[per-dir %s] force %s to have MIME-type " + "'%s'", perdir, r->filename, p->forced_mimetype); + ap_set_content_type(r, p->forced_mimetype); + } + } + return 2; + } + + /* + * Ok, now we finally know all patterns have matched and + * that there is something to replace, so we create the + * substitution URL string in `newuri'. + */ + do_expand(r, output, newuri, sizeof(newuri), briRR, briRC); + if (perdir == NULL) { + rewritelog(r, 2, "rewrite %s -> %s", uri, newuri); + } + else { + rewritelog(r, 2, "[per-dir %s] rewrite %s -> %s", perdir, uri, newuri); + } + + /* + * Additionally do expansion for the environment variable + * strings (`RewriteRule .. .. [E=<string>]'). + */ + do_expand_env(r, p->env, briRR, briRC); + + /* + * Also set cookies for any cookie strings + * (`RewriteRule .. .. [CO=<string>]'). + */ + do_expand_cookie(r, p->cookie, briRR, briRC); + + /* + * Now replace API's knowledge of the current URI: + * Replace r->filename with the new URI string and split out + * an on-the-fly generated QUERY_STRING part into r->args + */ + r->filename = apr_pstrdup(r->pool, newuri); + splitout_queryargs(r, p->flags & RULEFLAG_QSAPPEND); + + /* + * Add the previously stripped per-directory location + * prefix if the new URI is not a new one for this + * location, i.e. if it's not an absolute URL (!) path nor + * a fully qualified URL scheme. + */ + if ( perdir && !is_proxyreq && *r->filename != '/' + && !is_absolute_uri(r->filename)) { + rewritelog(r, 3, "[per-dir %s] add per-dir prefix: %s -> %s%s", + perdir, r->filename, perdir, r->filename); + r->filename = apr_pstrcat(r->pool, 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) { + fully_qualify_uri(r); + if (perdir == NULL) { + rewritelog(r, 2, "forcing proxy-throughput with %s", r->filename); + } + else { + rewritelog(r, 2, "[per-dir %s] forcing proxy-throughput with %s", + perdir, 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); + if (perdir == NULL) { + rewritelog(r, 2, + "explicitly forcing redirect with %s", r->filename); + } + else { + rewritelog(r, 2, + "[per-dir %s] explicitly forcing redirect with %s", + perdir, 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)) { + if (perdir == NULL) { + rewritelog(r, 2, + "implicitly forcing redirect (rc=%d) with %s", + p->forced_responsecode, r->filename); + } + else { + rewritelog(r, 2, "[per-dir %s] implicitly forcing redirect " + "(rc=%d) with %s", perdir, p->forced_responsecode, + r->filename); + } + r->status = p->forced_responsecode; + return 1; + } + + /* + * Finally we had to remember if a MIME-type should be + * forced for this URL (`RewriteRule .. .. [T=<type>]') + * Later in the API processing phase this is forced by our + * MIME API-hook function. This time it's no problem even for + * the per-directory context (where the MIME-type hook was + * already processed) because a sub-request happens ;-) + */ + if (p->forced_mimetype != NULL) { + apr_table_setn(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR, + p->forced_mimetype); + if (perdir == NULL) { + rewritelog(r, 2, "remember %s to have MIME-type '%s'", + r->filename, p->forced_mimetype); + } + else { + rewritelog(r, 2, + "[per-dir %s] remember %s to have MIME-type '%s'", + perdir, r->filename, p->forced_mimetype); + } + } + + /* + * Puuhhhhhhhh... WHAT COMPLICATED STUFF ;_) + * But now we're done for this particular rule. + */ + return 1; +} + +static int apply_rewrite_cond(request_rec *r, rewritecond_entry *p, + char *perdir, backrefinfo *briRR, + backrefinfo *briRC) +{ + char input[MAX_STRING_LEN]; + apr_finfo_t sb; + request_rec *rsub; + regmatch_t regmatch[AP_MAX_REG_MATCH]; + int rc; + + /* + * Construct the string we match against + */ + + do_expand(r, p->input, input, sizeof(input), briRR, briRC); + + /* + * Apply the patterns + */ + + rc = 0; + if (strcmp(p->pattern, "-f") == 0) { + if (apr_stat(&sb, input, APR_FINFO_MIN, r->pool) == APR_SUCCESS) { + if (sb.filetype == APR_REG) { + rc = 1; + } + } + } + else if (strcmp(p->pattern, "-s") == 0) { + if (apr_stat(&sb, input, APR_FINFO_MIN, r->pool) == APR_SUCCESS) { + if ((sb.filetype == APR_REG) && sb.size > 0) { + rc = 1; + } + } + } + else if (strcmp(p->pattern, "-l") == 0) { +#if !defined(OS2) + if (apr_lstat(&sb, input, APR_FINFO_MIN, r->pool) == APR_SUCCESS) { + if (sb.filetype == APR_LNK) { + rc = 1; + } + } +#endif + } + else if (strcmp(p->pattern, "-d") == 0) { + if (apr_stat(&sb, input, APR_FINFO_MIN, r->pool) == APR_SUCCESS) { + if (sb.filetype == APR_DIR) { + rc = 1; + } + } + } + else if (strcmp(p->pattern, "-U") == 0) { + /* avoid infinite subrequest recursion */ + if (strlen(input) > 0 && subreq_ok(r)) { + + /* run a URI-based subrequest */ + rsub = ap_sub_req_lookup_uri(input, r, NULL); + + /* URI exists for any result up to 3xx, redirects allowed */ + if (rsub->status < 400) + rc = 1; + + /* log it */ + rewritelog(r, 5, "RewriteCond URI (-U) check: " + "path=%s -> status=%d", input, rsub->status); + + /* cleanup by destroying the subrequest */ + ap_destroy_sub_req(rsub); + } + } + else if (strcmp(p->pattern, "-F") == 0) { + /* avoid infinite subrequest recursion */ + if (strlen(input) > 0 && subreq_ok(r)) { + + /* process a file-based subrequest: + * this differs from -U in that no path translation is done. + */ + rsub = ap_sub_req_lookup_file(input, r, NULL); + + /* file exists for any result up to 2xx, no redirects */ + 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; + } + + /* log it */ + rewritelog(r, 5, "RewriteCond file (-F) check: path=%s " + "-> file=%s status=%d", input, rsub->filename, + rsub->status); + + /* cleanup by destroying the subrequest */ + ap_destroy_sub_req(rsub); + } + } + else if (strlen(p->pattern) > 1 && *(p->pattern) == '>') { + rc = (compare_lexicography(input, p->pattern+1) == 1 ? 1 : 0); + } + else if (strlen(p->pattern) > 1 && *(p->pattern) == '<') { + rc = (compare_lexicography(input, p->pattern+1) == -1 ? 1 : 0); + } + else if (strlen(p->pattern) > 1 && *(p->pattern) == '=') { + if (strcmp(p->pattern+1, "\"\"") == 0) { + rc = (*input == '\0'); + } + else { + rc = (strcmp(input, p->pattern+1) == 0 ? 1 : 0); + } + } + else { + /* it is really a regexp pattern, so apply it */ + rc = (ap_regexec(p->regexp, input, AP_MAX_REG_MATCH, regmatch,0) == 0); + + /* if it isn't a negated pattern and really matched + we update the passed-through regex subst info structure */ + if (rc && !(p->flags & CONDFLAG_NOTMATCH)) { + briRC->source = apr_pstrdup(r->pool, input); + briRC->nsub = p->regexp->re_nsub; + memcpy((void *)(briRC->regmatch), (void *)(regmatch), + sizeof(regmatch)); + } + } + + /* if this is a non-matching regexp, just negate the result */ + if (p->flags & CONDFLAG_NOTMATCH) { + rc = !rc; + } + + rewritelog(r, 4, "RewriteCond: input='%s' pattern='%s%s' => %s", + input, (p->flags & CONDFLAG_NOTMATCH ? "!" : ""), + p->pattern, rc ? "matched" : "not-matched"); + + /* end just return the result */ + return rc; +} + + +/* +** +-------------------------------------------------------+ +** | | +** | URL transformation functions +** | | +** +-------------------------------------------------------+ +*/ + + +/* +** +** perform all the expansions on the input string +** leaving the result in the supplied buffer +** +*/ + +static void do_expand(request_rec *r, char *input, char *buffer, int nbuf, + backrefinfo *briRR, backrefinfo *briRC) +{ + char *inp, *outp; + apr_size_t span, space; + + /* + * 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. + */ + + inp = input; + outp = buffer; + space = nbuf - 1; /* room for '\0' */ + + for (;;) { + span = strcspn(inp, "\\$%"); + if (span > space) { + span = space; + } + memcpy(outp, inp, span); + inp += span; + outp += span; + space -= span; + if (space == 0 || *inp == '\0') { + break; + } + /* now we have a '\', '$', or '%' */ + if (inp[0] == '\\') { + if (inp[1] != '\0') { + inp++; + goto skip; + } + } + else if (inp[1] == '{') { + char *endp; + endp = find_closing_bracket(inp+2, '{', '}'); + if (endp == NULL) { + goto skip; + } + /* + * These lookups may be recursive in a very convoluted + * fashion -- see the LA-U and LA-F variable expansion + * prefixes -- so we copy lookup keys to a separate buffer + * rather than adding zero bytes in order to use them in + * place. + */ + if (inp[0] == '$') { + /* ${...} map lookup expansion */ + /* + * 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. + */ + char *map, *key, *dflt, *result; + char xkey[MAX_STRING_LEN]; + char xdflt[MAX_STRING_LEN]; + key = find_char_in_brackets(inp+2, ':', '{', '}'); + if (key == NULL) { + goto skip; + } + map = apr_pstrndup(r->pool, inp+2, key-inp-2); + dflt = find_char_in_brackets(key+1, '|', '{', '}'); + if (dflt == NULL) { + key = apr_pstrndup(r->pool, key+1, endp-key-1); + dflt = ""; + } + else { + key = apr_pstrndup(r->pool, key+1, dflt-key-1); + dflt = apr_pstrndup(r->pool, dflt+1, endp-dflt-1); + } + do_expand(r, key, xkey, sizeof(xkey), briRR, briRC); + result = lookup_map(r, map, xkey); + if (result) { + span = apr_cpystrn(outp, result, space) - outp; + } + else { + do_expand(r, dflt, xdflt, sizeof(xdflt), briRR, briRC); + span = apr_cpystrn(outp, xdflt, space) - outp; + } + } + else if (inp[0] == '%') { + /* %{...} variable lookup expansion */ + char *var; + var = apr_pstrndup(r->pool, inp+2, endp-inp-2); + span = apr_cpystrn(outp, lookup_variable(r, var), space) - outp; + } + else { + span = 0; + } + inp = endp+1; + outp += span; + space -= span; + continue; + } + else if (apr_isdigit(inp[1])) { + int n = inp[1] - '0'; + backrefinfo *bri = NULL; + if (inp[0] == '$') { + /* $N RewriteRule regexp backref expansion */ + bri = briRR; + } + else if (inp[0] == '%') { + /* %N RewriteCond regexp backref expansion */ + bri = briRC; + } + /* see ap_pregsub() in src/main/util.c */ + if (bri && 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 (span > space) { + span = space; + } + memcpy(outp, bri->source + bri->regmatch[n].rm_so, span); + outp += span; + space -= span; + } + inp += 2; + continue; + } + skip: + *outp++ = *inp++; + space--; + } + *outp++ = '\0'; +} + + +/* +** +** perform all the expansions on the environment variables +** +*/ + +static void do_expand_env(request_rec *r, char *env[], + backrefinfo *briRR, backrefinfo *briRC) +{ + int i; + char buf[MAX_STRING_LEN]; + + for (i = 0; env[i] != NULL; i++) { + do_expand(r, env[i], buf, sizeof(buf), briRR, briRC); + add_env_variable(r, buf); + } +} + +static void do_expand_cookie( request_rec *r, char *cookie[], + backrefinfo *briRR, backrefinfo *briRC) +{ + int i; + char buf[MAX_STRING_LEN]; + + for (i = 0; cookie[i] != NULL; i++) { + do_expand(r, cookie[i], buf, sizeof(buf), briRR, briRC); + add_cookie(r, buf); + } +} + + +/* +** +** split out a QUERY_STRING part from +** the current URI string +** +*/ + +static void splitout_queryargs(request_rec *r, int qsappend) +{ + char *q; + char *olduri; + + /* don't touch, unless it's an http or mailto URL. + * See RFC 1738 and RFC 2368. + */ + if ( is_absolute_uri(r->filename) + && strncasecmp(r->filename, "http", 4) + && strncasecmp(r->filename, "mailto", 6)) { + r->args = NULL; /* forget the query that's still flying around */ + return; + } + + q = strchr(r->filename, '?'); + if (q != NULL) { + olduri = apr_pstrdup(r->pool, r->filename); + *q++ = '\0'; + if (qsappend) { + r->args = apr_pstrcat(r->pool, q, "&", r->args, NULL); + } + else { + r->args = apr_pstrdup(r->pool, q); + } + if (strlen(r->args) == 0) { + r->args = NULL; + rewritelog(r, 3, "split uri=%s -> uri=%s, args=<none>", olduri, + r->filename); + } + else { + if (r->args[strlen(r->args)-1] == '&') { + r->args[strlen(r->args)-1] = '\0'; + } + rewritelog(r, 3, "split uri=%s -> uri=%s, args=%s", olduri, + r->filename, r->args); + } + } + + return; +} + + +/* +** +** strip 'http[s]://ourhost/' from URI +** +*/ + +static void reduce_uri(request_rec *r) +{ + char *cp; + unsigned short port; + char *portp; + char *hostp; + char *url; + char c; + char host[LONG_STRING_LEN]; + char buf[MAX_STRING_LEN]; + char *olduri; + apr_size_t l; + + cp = (char *)ap_http_method(r); + l = strlen(cp); + if ( strlen(r->filename) > l+3 + && strncasecmp(r->filename, cp, l) == 0 + && r->filename[l] == ':' + && r->filename[l+1] == '/' + && r->filename[l+2] == '/' ) { + /* there was really a rewrite to a remote path */ + + olduri = apr_pstrdup(r->pool, r->filename); /* save for logging */ + + /* cut the hostname and port out of the URI */ + apr_cpystrn(buf, r->filename+(l+3), sizeof(buf)); + hostp = buf; + for (cp = hostp; *cp != '\0' && *cp != '/' && *cp != ':'; cp++) + ; + if (*cp == ':') { + /* set host */ + *cp++ = '\0'; + apr_cpystrn(host, hostp, sizeof(host)); + /* set port */ + portp = cp; + for (; *cp != '\0' && *cp != '/'; cp++) + ; + c = *cp; + *cp = '\0'; + port = atoi(portp); + *cp = c; + /* set remaining url */ + url = cp; + } + else if (*cp == '/') { + /* set host */ + *cp = '\0'; + apr_cpystrn(host, hostp, sizeof(host)); + *cp = '/'; + /* set port */ + port = ap_default_port(r); + /* set remaining url */ + url = cp; + } + else { + /* set host */ + apr_cpystrn(host, hostp, sizeof(host)); + /* set port */ + port = ap_default_port(r); + /* set remaining url */ + url = "/"; + } + + /* now check whether we could reduce it to a local path... */ + if (ap_matches_request_vhost(r, host, port)) { + /* this is our host, so only the URL remains */ + r->filename = apr_pstrdup(r->pool, url); + rewritelog(r, 3, "reduce %s -> %s", olduri, r->filename); + } + } + return; +} + + +/* +** +** add 'http[s]://ourhost[:ourport]/' to URI +** if URI is still not fully qualified +** +*/ + +static void fully_qualify_uri(request_rec *r) +{ + char buf[32]; + const char *thisserver; + char *thisport; + int port; + + if (!is_absolute_uri(r->filename)) { + + thisserver = ap_get_server_name(r); + port = ap_get_server_port(r); + if (ap_is_default_port(port,r)) { + thisport = ""; + } + else { + apr_snprintf(buf, sizeof(buf), ":%u", port); + thisport = buf; + } + + if (r->filename[0] == '/') { + r->filename = apr_psprintf(r->pool, "%s://%s%s%s", + ap_http_method(r), thisserver, + thisport, r->filename); + } + else { + r->filename = apr_psprintf(r->pool, "%s://%s%s/%s", + ap_http_method(r), thisserver, + thisport, r->filename); + } + } + return; +} + + +/* return number of chars of the scheme (incl. '://') + * if the URI is absolute (includes a scheme etc.) + * otherwise 0. + * + * 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 should 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) +{ + /* fast exit */ + if (*uri == '/' || strlen(uri) <= 5) { + return 0; + } + + switch (*uri++) { + case 'f': + case 'F': + if (!strncasecmp(uri, "tp://", 5)) { /* ftp:// */ + return 6; + } + break; + + case 'g': + case 'G': + if (!strncasecmp(uri, "opher://", 8)) { /* gopher:// */ + return 9; + } + break; + + case 'h': + case 'H': + if (!strncasecmp(uri, "ttp://", 6)) { /* http:// */ + return 7; + } + else if (!strncasecmp(uri, "ttps://", 7)) { /* https:// */ + return 8; + } + break; + + case 'l': + case 'L': + if (!strncasecmp(uri, "dap://", 6)) { /* ldap:// */ + return 7; + } + break; + + case 'm': + case 'M': + if (!strncasecmp(uri, "ailto:", 6)) { /* mailto: */ + return 7; + } + break; + + case 'n': + case 'N': + if (!strncasecmp(uri, "ews:", 4)) { /* news: */ + return 5; + } + else if (!strncasecmp(uri, "ntp://", 6)) { /* nntp:// */ + return 7; + } + break; + } + + return 0; +} + + +/* 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 (!strncasecmp(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); +} + + +/* +** +** Expand tilde-paths (/~user) through Unix /etc/passwd +** database information (or other OS-specific database) +** +*/ +#if APR_HAS_USER +static char *expand_tildepaths(request_rec *r, char *uri) +{ + char user[LONG_STRING_LEN]; + char *newuri; + int i, j; + char *homedir; + + newuri = uri; + if (uri != NULL && strlen(uri) > 2 && uri[0] == '/' && uri[1] == '~') { + /* cut out the username */ + for (j = 0, i = 2; j < sizeof(user)-1 + && uri[i] != '\0' + && uri[i] != '/' ; ) { + user[j++] = uri[i++]; + } + user[j] = '\0'; + + /* lookup username in systems passwd file */ + if (apr_get_home_directory(&homedir, user, r->pool) == APR_SUCCESS) { + /* ok, user was found, so expand the ~user string */ + if (uri[i] != '\0') { + /* ~user/anything... has to be expanded */ + if (homedir[strlen(homedir)-1] == '/') { + homedir[strlen(homedir)-1] = '\0'; + } + newuri = apr_pstrcat(r->pool, homedir, uri+i, NULL); + } + else { + /* only ~user has to be expanded */ + newuri = homedir; + } + } + } + return newuri; +} +#endif /* if APR_HAS_USER */ + + + +/* +** +-------------------------------------------------------+ +** | | +** | DBM hashfile support +** | | +** +-------------------------------------------------------+ +*/ + + +static char *lookup_map(request_rec *r, char *name, char *key) +{ + rewrite_server_conf *conf; + apr_array_header_t *rewritemaps; + rewritemap_entry *entries; + rewritemap_entry *s; + char *value; + apr_finfo_t st; + apr_status_t rv; + int i; + + /* get map configuration */ + conf = ap_get_module_config(r->server->module_config, &rewrite_module); + rewritemaps = conf->rewritemaps; + + entries = (rewritemap_entry *)rewritemaps->elts; + for (i = 0; i < rewritemaps->nelts; i++) { + s = &entries[i]; + if (strcmp(s->name, name) == 0) { + if (s->type == MAPTYPE_TXT) { + if ((rv = apr_stat(&st, s->checkfile, + APR_FINFO_MIN, r->pool)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "mod_rewrite: can't access text RewriteMap " + "file %s", s->checkfile); + rewritelog(r, 1, "can't open RewriteMap file, " + "see error log"); + return NULL; + } + value = get_cache_string(cachep, s->cachename, CACHEMODE_TS, + st.mtime, key); + if (value == NULL) { + rewritelog(r, 6, "cache lookup FAILED, forcing new " + "map lookup"); + if ((value = + lookup_map_txtfile(r, s->datafile, key)) != NULL) { + rewritelog(r, 5, "map lookup OK: map=%s key=%s[txt] " + "-> val=%s", s->name, key, value); + set_cache_string(cachep, s->cachename, CACHEMODE_TS, + st.mtime, key, value); + return value; + } + else { + rewritelog(r, 5, "map lookup FAILED: map=%s[txt] " + "key=%s", s->name, key); + set_cache_string(cachep, s->cachename, CACHEMODE_TS, + st.mtime, key, ""); + return NULL; + } + } + else { + rewritelog(r, 5, "cache lookup OK: map=%s[txt] key=%s " + "-> val=%s", s->name, key, value); + return value[0] != '\0' ? value : NULL; + } + } + else if (s->type == MAPTYPE_DBM) { + if ((rv = apr_stat(&st, s->checkfile, + APR_FINFO_MIN, r->pool)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "mod_rewrite: can't access DBM RewriteMap " + "file %s", s->checkfile); + rewritelog(r, 1, "can't open DBM RewriteMap file, " + "see error log"); + return NULL; + } + value = get_cache_string(cachep, s->cachename, CACHEMODE_TS, + st.mtime, key); + if (value == NULL) { + rewritelog(r, 6, + "cache lookup FAILED, forcing new map lookup"); + if ((value = + lookup_map_dbmfile(r, s->datafile, s->dbmtype, key)) != NULL) { + rewritelog(r, 5, "map lookup OK: map=%s[dbm] key=%s " + "-> val=%s", s->name, key, value); + set_cache_string(cachep, s->cachename, CACHEMODE_TS, + st.mtime, key, value); + return value; + } + else { + rewritelog(r, 5, "map lookup FAILED: map=%s[dbm] " + "key=%s", s->name, key); + set_cache_string(cachep, s->cachename, CACHEMODE_TS, + st.mtime, key, ""); + return NULL; + } + } + else { + rewritelog(r, 5, "cache lookup OK: map=%s[dbm] key=%s " + "-> val=%s", s->name, key, value); + return value[0] != '\0' ? value : NULL; + } + } + else if (s->type == MAPTYPE_PRG) { + if ((value = + lookup_map_program(r, s->fpin, s->fpout, key)) != NULL) { + rewritelog(r, 5, "map lookup OK: map=%s key=%s -> val=%s", + s->name, key, value); + return value; + } + else { + rewritelog(r, 5, "map lookup FAILED: map=%s key=%s", + s->name, key); + } + } + else if (s->type == MAPTYPE_INT) { + if ((value = s->func(r, key)) != NULL) { + rewritelog(r, 5, "map lookup OK: map=%s key=%s -> val=%s", + s->name, key, value); + return value; + } + else { + rewritelog(r, 5, "map lookup FAILED: map=%s key=%s", + s->name, key); + } + } + else if (s->type == MAPTYPE_RND) { + if ((rv = apr_stat(&st, s->checkfile, + APR_FINFO_MIN, r->pool)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "mod_rewrite: can't access text RewriteMap " + "file %s", s->checkfile); + rewritelog(r, 1, "can't open RewriteMap file, " + "see error log"); + return NULL; + } + value = get_cache_string(cachep, s->cachename, CACHEMODE_TS, + st.mtime, key); + if (value == NULL) { + rewritelog(r, 6, "cache lookup FAILED, forcing new " + "map lookup"); + if ((value = + lookup_map_txtfile(r, s->datafile, key)) != NULL) { + rewritelog(r, 5, "map lookup OK: map=%s key=%s[txt] " + "-> val=%s", s->name, key, value); + set_cache_string(cachep, s->cachename, CACHEMODE_TS, + st.mtime, key, value); + } + else { + rewritelog(r, 5, "map lookup FAILED: map=%s[txt] " + "key=%s", s->name, key); + set_cache_string(cachep, s->cachename, CACHEMODE_TS, + st.mtime, key, ""); + return NULL; + } + } + else { + rewritelog(r, 5, "cache lookup OK: map=%s[txt] key=%s " + "-> val=%s", s->name, key, value); + } + if (value[0] != '\0') { + value = select_random_value_part(r, value); + rewritelog(r, 5, "randomly choosen the subvalue `%s'", + value); + } + else { + value = NULL; + } + return value; + } + } + } + return NULL; +} + +static char *lookup_map_txtfile(request_rec *r, const char *file, char *key) +{ + apr_file_t *fp = NULL; + apr_status_t rc; + char line[1024]; + char *value = NULL; + char *cpT; + apr_size_t skip; + char *curkey; + char *curval; + + rc = apr_file_open(&fp, file, APR_READ|APR_BUFFERED, APR_OS_DEFAULT, r->pool); + if (rc != APR_SUCCESS) { + return NULL; + } + + while (apr_file_gets(line, sizeof(line), fp) == APR_SUCCESS) { + if (line[0] == '#') { + continue; /* ignore comments */ + } + cpT = line; + curkey = cpT; + skip = strcspn(cpT," \t\r\n"); + if (skip == 0) { + continue; /* ignore lines that start with a space, tab, CR, or LF */ + } + cpT += skip; + *cpT = '\0'; + if (strcmp(curkey, key) != 0) { + continue; /* key does not match... */ + } + + /* found a matching key; now extract and return the value */ + ++cpT; + skip = strspn(cpT, " \t\r\n"); + cpT += skip; + curval = cpT; + skip = strcspn(cpT, " \t\r\n"); + if (skip == 0) { + continue; /* no value... */ + } + cpT += skip; + *cpT = '\0'; + value = apr_pstrdup(r->pool, curval); + break; + } + apr_file_close(fp); + return value; +} + +static char *lookup_map_dbmfile(request_rec *r, const char *file, + const char *dbmtype, char *key) +{ + apr_dbm_t *dbmfp = NULL; + apr_datum_t dbmkey; + apr_datum_t dbmval; + char *value; + + if (apr_dbm_open_ex(&dbmfp, dbmtype, file, APR_DBM_READONLY, APR_OS_DEFAULT, + r->pool) != APR_SUCCESS) { + return NULL; + } + + 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_program(request_rec *r, apr_file_t *fpin, + apr_file_t *fpout, char *key) +{ + char buf[LONG_STRING_LEN]; + char c; + int i; + apr_size_t nbytes; + apr_status_t rv; + +#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, + "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); + apr_file_write(fpin, key, &nbytes); + nbytes = 1; + apr_file_write(fpin, "\n", &nbytes); +#else + iova[0].iov_base = key; + iova[0].iov_len = strlen(key); + iova[1].iov_base = "\n"; + iova[1].iov_len = 1; + + niov = 2; + apr_file_writev(fpin, iova, niov, &nbytes); +#endif + + /* read in the response value */ + i = 0; + nbytes = 1; + apr_file_read(fpout, &c, &nbytes); + while (nbytes == 1 && (i < LONG_STRING_LEN-1)) { + if (c == '\n') { + break; + } + buf[i++] = c; + + apr_file_read(fpout, &c, &nbytes); + } + 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, + "apr_global_mutex_unlock(rewrite_mapr_lock_acquire) " + "failed"); + return NULL; /* Maybe this should be fatal? */ + } + } + + if (strcasecmp(buf, "NULL") == 0) { + return NULL; + } + else { + return apr_pstrdup(r->pool, buf); + } +} + +static void ap_register_rewrite_mapfunc(char *name, rewrite_mapfunc_t *func) +{ + apr_hash_set(mapfunc_hash, name, strlen(name), (const void *)func); +} + +static char *rewrite_mapfunc_toupper(request_rec *r, char *key) +{ + char *value, *cp; + + for (cp = value = apr_pstrdup(r->pool, key); cp != NULL && *cp != '\0'; + cp++) { + *cp = apr_toupper(*cp); + } + return value; +} + +static char *rewrite_mapfunc_tolower(request_rec *r, char *key) +{ + char *value, *cp; + + for (cp = value = apr_pstrdup(r->pool, key); cp != NULL && *cp != '\0'; + cp++) { + *cp = apr_tolower(*cp); + } + return value; +} + +static char *rewrite_mapfunc_escape(request_rec *r, char *key) +{ + char *value; + + value = ap_escape_uri(r->pool, key); + return value; +} + +static char *rewrite_mapfunc_unescape(request_rec *r, char *key) +{ + char *value; + + value = apr_pstrdup(r->pool, key); + ap_unescape_url(value); + return value; +} + +static int rewrite_rand_init_done = 0; + +static void rewrite_rand_init(void) +{ + if (!rewrite_rand_init_done) { + srand((unsigned)(getpid())); + rewrite_rand_init_done = 1; + } + return; +} + +static int rewrite_rand(int l, int h) +{ + rewrite_rand_init(); + + /* Get [0,1) and then scale to the appropriate range. Note that using + * a floating point value ensures that we use all bits of the rand() + * result. Doing an integer modulus would only use the lower-order bits + * which may not be as uniformly random. + */ + return (int)(((double)(rand() % RAND_MAX) / RAND_MAX) * (h - l + 1) + l); +} + +static char *select_random_value_part(request_rec *r, char *value) +{ + char *buf; + int n, i, k; + + /* count number of distinct values */ + for (n = 1, i = 0; value[i] != '\0'; i++) { + if (value[i] == '|') { + n++; + } + } + + /* when only one value we have no option to choose */ + if (n == 1) { + return value; + } + + /* else randomly select one */ + k = rewrite_rand(1, n); + + /* and grep it out */ + for (n = 1, i = 0; value[i] != '\0'; i++) { + if (n == k) { + break; + } + if (value[i] == '|') { + n++; + } + } + buf = apr_pstrdup(r->pool, &value[i]); + for (i = 0; buf[i] != '\0' && buf[i] != '|'; i++) + ; + buf[i] = '\0'; + return buf; +} + + +/* +** +-------------------------------------------------------+ +** | | +** | rewriting logfile support +** | | +** +-------------------------------------------------------+ +*/ + + +static int open_rewritelog(server_rec *s, apr_pool_t *p) +{ + rewrite_server_conf *conf; + const char *fname; + apr_status_t rc; + piped_log *pl; + int rewritelog_flags = ( APR_WRITE | APR_APPEND | APR_CREATE ); + apr_fileperms_t rewritelog_mode = ( APR_UREAD | APR_UWRITE | + APR_GREAD | APR_WREAD ); + + conf = ap_get_module_config(s->module_config, &rewrite_module); + + /* - no logfile configured + * - logfilename empty + * - virtual log shared w/ main server + */ + if (!conf->rewritelogfile || !*conf->rewritelogfile || conf->rewritelogfp) { + return 1; + } + + if (*conf->rewritelogfile == '|') { + if ((pl = ap_open_piped_log(p, conf->rewritelogfile+1)) == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "mod_rewrite: could not open reliable pipe " + "to RewriteLog filter %s", conf->rewritelogfile+1); + return 0; + } + conf->rewritelogfp = ap_piped_log_write_fd(pl); + } + else if (*conf->rewritelogfile != '\0') { + fname = ap_server_root_relative(p, conf->rewritelogfile); + if (!fname) { + ap_log_error(APLOG_MARK, APLOG_ERR, APR_EBADPATH, s, + "mod_rewrite: Invalid RewriteLog " + "path %s", conf->rewritelogfile); + return 0; + } + if ((rc = apr_file_open(&conf->rewritelogfp, fname, + rewritelog_flags, rewritelog_mode, p)) + != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rc, s, + "mod_rewrite: could not open RewriteLog " + "file %s", fname); + return 0; + } + } + + return 1; +} + +static void rewritelog(request_rec *r, int level, const char *text, ...) +{ + rewrite_server_conf *conf; + conn_rec *conn; + char *str1; + char str2[512]; + char str3[1024]; + const char *type; + char redir[20]; /* enough for "/redir#%d" if int is 32 bit */ + va_list ap; + int i; + apr_size_t nbytes; + request_rec *req; + char *ruser; + const char *rhost; + apr_status_t rv; + + va_start(ap, text); + conf = ap_get_module_config(r->server->module_config, &rewrite_module); + conn = r->connection; + + if (conf->rewritelogfp == NULL) { + return; + } + if (conf->rewritelogfile == NULL) { + return; + } + if (*(conf->rewritelogfile) == '\0') { + return; + } + + if (level > conf->rewriteloglevel) { + return; + } + + if (r->user == NULL) { + ruser = "-"; + } + else if (strlen(r->user) != 0) { + ruser = r->user; + } + else { + ruser = "\"\""; + } + + rhost = ap_get_remote_host(conn, r->per_dir_config, + REMOTE_NOLOOKUP, NULL); + if (rhost == NULL) { + rhost = "UNKNOWN-HOST"; + } + + str1 = apr_pstrcat(r->pool, rhost, " ", + (conn->remote_logname != NULL ? + conn->remote_logname : "-"), " ", + ruser, NULL); + apr_vsnprintf(str2, sizeof(str2), text, ap); + + if (r->main == NULL) { + type = "initial"; + } + else { + type = "subreq"; + } + + for (i = 0, req = r; req->prev != NULL; req = req->prev) { + i++; + } + if (i == 0) { + redir[0] = '\0'; + } + else { + apr_snprintf(redir, sizeof(redir), "/redir#%d", i); + } + + apr_snprintf(str3, sizeof(str3), + "%s %s [%s/sid#%lx][rid#%lx/%s%s] (%d) %s" APR_EOL_STR, str1, + current_logtime(r), ap_get_server_name(r), + (unsigned long)(r->server), (unsigned long)r, + type, redir, level, str2); + + rv = apr_global_mutex_lock(rewrite_log_lock); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "apr_global_mutex_lock(rewrite_log_lock) failed"); + /* XXX: Maybe this should be fatal? */ + } + nbytes = strlen(str3); + apr_file_write(conf->rewritelogfp, str3, &nbytes); + rv = apr_global_mutex_unlock(rewrite_log_lock); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "apr_global_mutex_unlock(rewrite_log_lock) failed"); + /* XXX: Maybe this should be fatal? */ + } + + va_end(ap); + return; +} + +static char *current_logtime(request_rec *r) +{ + apr_time_exp_t t; + char tstr[80]; + apr_size_t len; + + apr_time_exp_lt(&t, apr_time_now()); + + apr_strftime(tstr, &len, 80, "[%d/%b/%Y:%H:%M:%S ", &t); + apr_snprintf(tstr + strlen(tstr), 80-strlen(tstr), "%c%.2d%.2d]", + t.tm_gmtoff < 0 ? '-' : '+', + t.tm_gmtoff / (60*60), t.tm_gmtoff % (60*60)); + return apr_pstrdup(r->pool, tstr); +} + + + + +/* +** +-------------------------------------------------------+ +** | | +** | rewriting lockfile support +** | | +** +-------------------------------------------------------+ +*/ + +#define REWRITELOCK_MODE ( APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD ) + +static apr_status_t rewritelock_create(server_rec *s, apr_pool_t *p) +{ + apr_status_t rc; + + /* only operate if a lockfile is used */ + if (lockname == NULL || *(lockname) == '\0') { + return APR_SUCCESS; + } + + /* create the lockfile */ + rc = apr_global_mutex_create(&rewrite_mapr_lock_acquire, lockname, + APR_LOCK_DEFAULT, p); + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rc, s, + "mod_rewrite: Parent could not create RewriteLock " + "file %s", lockname); + return rc; + } + +#ifdef MOD_REWRITE_SET_MUTEX_PERMS + rc = unixd_set_global_mutex_perms(rewrite_mapr_lock_acquire); + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rc, s, + "mod_rewrite: Parent could not set permissions " + "on RewriteLock; check User and Group directives"); + return rc; + } +#endif + + return APR_SUCCESS; +} + +static apr_status_t rewritelock_remove(void *data) +{ + /* only operate if a lockfile is used */ + if (lockname == NULL || *(lockname) == '\0') { + return APR_SUCCESS; + } + + /* destroy the rewritelock */ + apr_global_mutex_destroy (rewrite_mapr_lock_acquire); + rewrite_mapr_lock_acquire = NULL; + lockname = NULL; + return(0); +} + + +/* +** +-------------------------------------------------------+ +** | | +** | program map support +** | | +** +-------------------------------------------------------+ +*/ + +static apr_status_t run_rewritemap_programs(server_rec *s, apr_pool_t *p) +{ + rewrite_server_conf *conf; + apr_array_header_t *rewritemaps; + rewritemap_entry *entries; + int i; + 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; + } + + rewritemaps = conf->rewritemaps; + entries = (rewritemap_entry *)rewritemaps->elts; + for (i = 0; i < rewritemaps->nelts; i++) { + apr_file_t *fpin = NULL; + apr_file_t *fpout = NULL; + rewritemap_entry *map = &entries[i]; + + if (map->type != MAPTYPE_PRG) { + continue; + } + if (map->argv[0] == NULL + || *(map->argv[0]) == '\0' + || map->fpin != NULL + || map->fpout != NULL ) { + continue; + } + rc = rewritemap_program_child(p, map->argv[0], map->argv, + &fpout, &fpin); + if (rc != APR_SUCCESS || fpin == NULL || fpout == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, rc, s, + "mod_rewrite: could not startup RewriteMap " + "program %s", map->datafile); + return rc; + } + map->fpin = fpin; + map->fpout = fpout; + } + return APR_SUCCESS; +} + +/* child process code */ +static apr_status_t rewritemap_program_child(apr_pool_t *p, + const char *progname, char **argv, + apr_file_t **fpout, + apr_file_t **fpin) +{ + apr_status_t rc; + apr_procattr_t *procattr; + apr_proc_t *procnew; + + if (((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]))) + != APR_SUCCESS) || + ((rc = apr_procattr_cmdtype_set(procattr, APR_PROGRAM)) + != APR_SUCCESS)) { + /* Something bad happened, give up and go away. */ + } + else { + 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); +} + + + + +/* +** +-------------------------------------------------------+ +** | | +** | environment variable support +** | | +** +-------------------------------------------------------+ +*/ + + +static char *lookup_variable(request_rec *r, char *var) +{ + const char *result; + char resultbuf[LONG_STRING_LEN]; + apr_time_exp_t tm; + request_rec *rsub; + + result = NULL; + + /* HTTP headers */ + if (strcasecmp(var, "HTTP_USER_AGENT") == 0) { + result = lookup_header(r, "User-Agent"); + } + else if (strcasecmp(var, "HTTP_REFERER") == 0) { + result = lookup_header(r, "Referer"); + } + else if (strcasecmp(var, "HTTP_COOKIE") == 0) { + result = lookup_header(r, "Cookie"); + } + else if (strcasecmp(var, "HTTP_FORWARDED") == 0) { + result = lookup_header(r, "Forwarded"); + } + else if (strcasecmp(var, "HTTP_HOST") == 0) { + result = lookup_header(r, "Host"); + } + else if (strcasecmp(var, "HTTP_PROXY_CONNECTION") == 0) { + result = lookup_header(r, "Proxy-Connection"); + } + else if (strcasecmp(var, "HTTP_ACCEPT") == 0) { + result = lookup_header(r, "Accept"); + } + /* all other headers from which we are still not know about */ + else if (strlen(var) > 5 && strncasecmp(var, "HTTP:", 5) == 0) { + result = lookup_header(r, var+5); + } + + /* connection stuff */ + else if (strcasecmp(var, "REMOTE_ADDR") == 0) { + result = r->connection->remote_ip; + } + else if (strcasecmp(var, "REMOTE_PORT") == 0) { + return apr_itoa(r->pool, r->connection->remote_addr->port); + } + else if (strcasecmp(var, "REMOTE_HOST") == 0) { + result = (char *)ap_get_remote_host(r->connection, + r->per_dir_config, REMOTE_NAME, NULL); + } + else if (strcasecmp(var, "REMOTE_USER") == 0) { + result = r->user; + } + else if (strcasecmp(var, "REMOTE_IDENT") == 0) { + result = (char *)ap_get_remote_logname(r); + } + + /* request stuff */ + else if (strcasecmp(var, "THE_REQUEST") == 0) { /* non-standard */ + result = r->the_request; + } + else if (strcasecmp(var, "REQUEST_METHOD") == 0) { + result = r->method; + } + else if (strcasecmp(var, "REQUEST_URI") == 0) { /* non-standard */ + result = r->uri; + } + else if (strcasecmp(var, "SCRIPT_FILENAME") == 0 || + strcasecmp(var, "REQUEST_FILENAME") == 0 ) { + result = r->filename; + } + else if (strcasecmp(var, "PATH_INFO") == 0) { + result = r->path_info; + } + else if (strcasecmp(var, "QUERY_STRING") == 0) { + result = r->args; + } + else if (strcasecmp(var, "AUTH_TYPE") == 0) { + result = r->ap_auth_type; + } + else if (strcasecmp(var, "IS_SUBREQ") == 0) { /* non-standard */ + result = (r->main != NULL ? "true" : "false"); + } + + /* internal server stuff */ + else if (strcasecmp(var, "DOCUMENT_ROOT") == 0) { + result = ap_document_root(r); + } + else if (strcasecmp(var, "SERVER_ADMIN") == 0) { + result = r->server->server_admin; + } + else if (strcasecmp(var, "SERVER_NAME") == 0) { + result = ap_get_server_name(r); + } + else if (strcasecmp(var, "SERVER_ADDR") == 0) { /* non-standard */ + result = r->connection->local_ip; + } + else if (strcasecmp(var, "SERVER_PORT") == 0) { + apr_snprintf(resultbuf, sizeof(resultbuf), "%u", ap_get_server_port(r)); + result = resultbuf; + } + else if (strcasecmp(var, "SERVER_PROTOCOL") == 0) { + result = r->protocol; + } + else if (strcasecmp(var, "SERVER_SOFTWARE") == 0) { + result = ap_get_server_version(); + } + else if (strcasecmp(var, "API_VERSION") == 0) { /* non-standard */ + apr_snprintf(resultbuf, sizeof(resultbuf), "%d:%d", + MODULE_MAGIC_NUMBER_MAJOR, MODULE_MAGIC_NUMBER_MINOR); + result = resultbuf; + } + +/* XXX: wow this has gotta be slow if you actually use it for a lot, recalculates exploded time for each variable */ + /* underlaying Unix system stuff */ + else if (strcasecmp(var, "TIME_YEAR") == 0) { + apr_time_exp_lt(&tm, apr_time_now()); + apr_snprintf(resultbuf, sizeof(resultbuf), "%04d", tm.tm_year + 1900); + result = resultbuf; + } +#define MKTIMESTR(format, tmfield) \ + apr_time_exp_lt(&tm, apr_time_now()); \ + apr_snprintf(resultbuf, sizeof(resultbuf), format, tm.tmfield); \ + result = resultbuf; + else if (strcasecmp(var, "TIME_MON") == 0) { + MKTIMESTR("%02d", tm_mon+1) + } + else if (strcasecmp(var, "TIME_DAY") == 0) { + MKTIMESTR("%02d", tm_mday) + } + else if (strcasecmp(var, "TIME_HOUR") == 0) { + MKTIMESTR("%02d", tm_hour) + } + else if (strcasecmp(var, "TIME_MIN") == 0) { + MKTIMESTR("%02d", tm_min) + } + else if (strcasecmp(var, "TIME_SEC") == 0) { + MKTIMESTR("%02d", tm_sec) + } + else if (strcasecmp(var, "TIME_WDAY") == 0) { + MKTIMESTR("%d", tm_wday) + } + else if (strcasecmp(var, "TIME") == 0) { + apr_time_exp_lt(&tm, apr_time_now()); + apr_snprintf(resultbuf, sizeof(resultbuf), + "%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); + result = resultbuf; + rewritelog(r, 1, "RESULT='%s'", result); + } + + /* all other env-variables from the parent Apache process */ + else if (strlen(var) > 4 && strncasecmp(var, "ENV:", 4) == 0) { + /* first try the internal Apache notes structure */ + result = apr_table_get(r->notes, var+4); + /* second try the internal Apache env structure */ + if (result == NULL) { + result = apr_table_get(r->subprocess_env, var+4); + } + /* third try the external OS env */ + if (result == NULL) { + result = getenv(var+4); + } + } + else if (strlen(var) > 4 && !strncasecmp(var, "SSL:", 4) + && rewrite_ssl_lookup) { + result = rewrite_ssl_lookup(r->pool, r->server, r->connection, r, + var + 4); + } + +#define LOOKAHEAD(subrecfunc) \ + if ( \ + /* filename is safe to use */ \ + r->filename != NULL \ + /* - and we're either not in a subrequest */ \ + && ( r->main == NULL \ + /* - or in a subrequest where paths are non-NULL... */ \ + || ( r->main->uri != NULL && r->uri != NULL \ + /* ...and sub and main paths differ */ \ + && strcmp(r->main->uri, r->uri) != 0))) { \ + /* process a file-based subrequest */ \ + rsub = subrecfunc(r->filename, r, NULL); \ + /* now recursively lookup the variable in the sub_req */ \ + result = lookup_variable(rsub, var+5); \ + /* copy it up to our scope before we destroy sub_req's apr_pool_t */ \ + result = apr_pstrdup(r->pool, result); \ + /* cleanup by destroying the subrequest */ \ + ap_destroy_sub_req(rsub); \ + /* log it */ \ + rewritelog(r, 5, "lookahead: path=%s var=%s -> val=%s", \ + r->filename, var+5, result); \ + /* return ourself to prevent re-pstrdup */ \ + return (char *)result; \ + } + + /* look-ahead for parameter through URI-based sub-request */ + else if (strlen(var) > 5 && strncasecmp(var, "LA-U:", 5) == 0) { + LOOKAHEAD(ap_sub_req_lookup_uri) + } + /* look-ahead for parameter through file-based sub-request */ + else if (strlen(var) > 5 && strncasecmp(var, "LA-F:", 5) == 0) { + LOOKAHEAD(ap_sub_req_lookup_file) + } + + /* file stuff */ + else if (strcasecmp(var, "SCRIPT_USER") == 0) { + result = "<unknown>"; + if (r->finfo.valid & APR_FINFO_USER) { + apr_get_username((char **)&result, r->finfo.user, r->pool); + } + } + else if (strcasecmp(var, "SCRIPT_GROUP") == 0) { + result = "<unknown>"; + if (r->finfo.valid & APR_FINFO_GROUP) { + apr_group_name_get((char **)&result, r->finfo.group, r->pool); + } + } else if (strcasecmp(var, "HTTPS") == 0) { + int flag = rewrite_is_https && rewrite_is_https(r->connection); + result = flag ? "on" : "off"; + } + + if (result == NULL) { + return apr_pstrdup(r->pool, ""); + } + else { + return apr_pstrdup(r->pool, result); + } +} + +static char *lookup_header(request_rec *r, const char *name) +{ + const apr_array_header_t *hdrs_arr; + const apr_table_entry_t *hdrs; + int i; + + hdrs_arr = apr_table_elts(r->headers_in); + hdrs = (const apr_table_entry_t *)hdrs_arr->elts; + for (i = 0; i < hdrs_arr->nelts; ++i) { + if (hdrs[i].key == NULL) { + continue; + } + if (strcasecmp(hdrs[i].key, name) == 0) { + apr_table_merge(r->notes, VARY_KEY_THIS, name); + return hdrs[i].val; + } + } + return NULL; +} + + + + +/* +** +-------------------------------------------------------+ +** | | +** | caching support +** | | +** +-------------------------------------------------------+ +*/ + + +static cache *init_cache(apr_pool_t *p) +{ + cache *c; + + c = (cache *)apr_palloc(p, sizeof(cache)); + if (apr_pool_create(&c->pool, p) != APR_SUCCESS) { + return NULL; + } + c->lists = apr_array_make(c->pool, 2, sizeof(cachelist)); +#if APR_HAS_THREADS + (void)apr_thread_mutex_create(&(c->lock), APR_THREAD_MUTEX_DEFAULT, p); +#endif + return c; +} + +static void set_cache_string(cache *c, const char *res, int mode, apr_time_t t, + char *key, char *value) +{ + cacheentry ce; + + ce.time = t; + ce.key = key; + ce.value = value; + store_cache_string(c, res, &ce); + return; +} + +static char *get_cache_string(cache *c, const char *res, int mode, + apr_time_t t, char *key) +{ + cacheentry *ce; + + ce = retrieve_cache_string(c, res, key); + if (ce == NULL) { + return NULL; + } + if (mode & CACHEMODE_TS) { + if (t != ce->time) { + return NULL; + } + } + else if (mode & CACHEMODE_TTL) { + if (t > ce->time) { + return NULL; + } + } + return ce->value; +} + +static int cache_tlb_hash(char *key) +{ + unsigned long n; + char *p; + + n = 0; + for (p = key; *p != '\0'; p++) { + n = ((n << 5) + n) ^ (unsigned long)(*p++); + } + + return n % CACHE_TLB_ROWS; +} + +static cacheentry *cache_tlb_lookup(cachetlbentry *tlb, cacheentry *elt, + char *key) +{ + int ix = cache_tlb_hash(key); + int i; + int j; + + for (i=0; i < CACHE_TLB_COLS; ++i) { + j = tlb[ix].t[i]; + if (j < 0) + return NULL; + if (strcmp(elt[j].key, key) == 0) + return &elt[j]; + } + return NULL; +} + +static void cache_tlb_replace(cachetlbentry *tlb, cacheentry *elt, + cacheentry *e) +{ + int ix = cache_tlb_hash(e->key); + int i; + + tlb = &tlb[ix]; + + for (i=1; i < CACHE_TLB_COLS; ++i) + tlb->t[i] = tlb->t[i-1]; + + tlb->t[0] = e - elt; +} + +static void store_cache_string(cache *c, const char *res, cacheentry *ce) +{ + int i; + int j; + cachelist *l; + cacheentry *e; + cachetlbentry *t; + int found_list; + +#if APR_HAS_THREADS + apr_thread_mutex_lock(c->lock); +#endif + + found_list = 0; + /* first try to edit an existing entry */ + for (i = 0; i < c->lists->nelts; i++) { + l = &(((cachelist *)c->lists->elts)[i]); + if (strcmp(l->resource, res) == 0) { + found_list = 1; + + e = cache_tlb_lookup((cachetlbentry *)l->tlb->elts, + (cacheentry *)l->entries->elts, ce->key); + if (e != NULL) { + e->time = ce->time; + e->value = apr_pstrdup(c->pool, ce->value); +#if APR_HAS_THREADS + apr_thread_mutex_unlock(c->lock); +#endif + return; + } + + for (j = 0; j < l->entries->nelts; j++) { + e = &(((cacheentry *)l->entries->elts)[j]); + if (strcmp(e->key, ce->key) == 0) { + e->time = ce->time; + e->value = apr_pstrdup(c->pool, ce->value); + cache_tlb_replace((cachetlbentry *)l->tlb->elts, + (cacheentry *)l->entries->elts, e); +#if APR_HAS_THREADS + apr_thread_mutex_unlock(c->lock); +#endif + return; + } + } + } + } + + /* create a needed new list */ + if (!found_list) { + l = apr_array_push(c->lists); + l->resource = apr_pstrdup(c->pool, res); + l->entries = apr_array_make(c->pool, 2, sizeof(cacheentry)); + l->tlb = apr_array_make(c->pool, CACHE_TLB_ROWS, + sizeof(cachetlbentry)); + for (i=0; i<CACHE_TLB_ROWS; ++i) { + t = &((cachetlbentry *)l->tlb->elts)[i]; + for (j=0; j<CACHE_TLB_COLS; ++j) + t->t[j] = -1; + } + } + + /* create the new entry */ + for (i = 0; i < c->lists->nelts; i++) { + l = &(((cachelist *)c->lists->elts)[i]); + if (strcmp(l->resource, res) == 0) { + e = apr_array_push(l->entries); + e->time = ce->time; + e->key = apr_pstrdup(c->pool, ce->key); + e->value = apr_pstrdup(c->pool, ce->value); + cache_tlb_replace((cachetlbentry *)l->tlb->elts, + (cacheentry *)l->entries->elts, e); +#if APR_HAS_THREADS + apr_thread_mutex_unlock(c->lock); +#endif + return; + } + } + + /* not reached, but when it is no problem... */ +#if APR_HAS_THREADS + apr_thread_mutex_unlock(c->lock); +#endif + return; +} + +static cacheentry *retrieve_cache_string(cache *c, const char *res, char *key) +{ + int i; + int j; + cachelist *l; + cacheentry *e; + +#if APR_HAS_THREADS + apr_thread_mutex_lock(c->lock); +#endif + + for (i = 0; i < c->lists->nelts; i++) { + l = &(((cachelist *)c->lists->elts)[i]); + if (strcmp(l->resource, res) == 0) { + + e = cache_tlb_lookup((cachetlbentry *)l->tlb->elts, + (cacheentry *)l->entries->elts, key); + if (e != NULL) { +#if APR_HAS_THREADS + apr_thread_mutex_unlock(c->lock); +#endif + return e; + } + + for (j = 0; j < l->entries->nelts; j++) { + e = &(((cacheentry *)l->entries->elts)[j]); + if (strcmp(e->key, key) == 0) { +#if APR_HAS_THREADS + apr_thread_mutex_unlock(c->lock); +#endif + return e; + } + } + } + } +#if APR_HAS_THREADS + apr_thread_mutex_unlock(c->lock); +#endif + return NULL; +} + + + + +/* +** +-------------------------------------------------------+ +** | | +** | misc functions +** | | +** +-------------------------------------------------------+ +*/ + +/* + * substitute the prefix path 'match' in 'input' with 'subst' + * (think of RewriteBase which substitutes the physical path with + * the virtual path) + */ + +static char *subst_prefix_path(request_rec *r, char *input, 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, "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, "add subst prefix: %s -> %s", input+len, output); + + return output; + } + + /* prefix didn't match */ + return input; +} + + +/* +** +** own command line parser which don't have the '\\' problem +** +*/ + +static int parseargline(char *str, char **a1, char **a2, char **a3) +{ + char *cp; + int isquoted; + +#define SKIP_WHITESPACE(cp) \ + for ( ; *cp == ' ' || *cp == '\t'; ) { \ + cp++; \ + }; + +#define CHECK_QUOTATION(cp,isquoted) \ + isquoted = 0; \ + if (*cp == '"') { \ + isquoted = 1; \ + cp++; \ + } + +#define DETERMINE_NEXTSTRING(cp,isquoted) \ + for ( ; *cp != '\0'; cp++) { \ + if ( (isquoted && (*cp == ' ' || *cp == '\t')) \ + || (*cp == '\\' && (*(cp+1) == ' ' || *(cp+1) == '\t'))) { \ + cp++; \ + continue; \ + } \ + if ( (!isquoted && (*cp == ' ' || *cp == '\t')) \ + || (isquoted && *cp == '"') ) { \ + break; \ + } \ + } + + cp = str; + SKIP_WHITESPACE(cp); + + /* determine first argument */ + CHECK_QUOTATION(cp, isquoted); + *a1 = cp; + DETERMINE_NEXTSTRING(cp, isquoted); + if (*cp == '\0') { + return 1; + } + *cp++ = '\0'; + + SKIP_WHITESPACE(cp); + + /* determine second argument */ + CHECK_QUOTATION(cp, isquoted); + *a2 = cp; + DETERMINE_NEXTSTRING(cp, isquoted); + if (*cp == '\0') { + *cp++ = '\0'; + *a3 = NULL; + return 0; + } + *cp++ = '\0'; + + SKIP_WHITESPACE(cp); + + /* again check if there are only two arguments */ + if (*cp == '\0') { + *cp++ = '\0'; + *a3 = NULL; + return 0; + } + + /* determine second argument */ + CHECK_QUOTATION(cp, isquoted); + *a3 = cp; + DETERMINE_NEXTSTRING(cp, isquoted); + *cp++ = '\0'; + + return 0; +} + + +static void add_env_variable(request_rec *r, char *s) +{ + char var[MAX_STRING_LEN]; + char val[MAX_STRING_LEN]; + char *cp; + int n; + + if ((cp = strchr(s, ':')) != NULL) { + n = ((cp-s) > MAX_STRING_LEN-1 ? MAX_STRING_LEN-1 : (cp-s)); + memcpy(var, s, n); + var[n] = '\0'; + apr_cpystrn(val, cp+1, sizeof(val)); + apr_table_set(r->subprocess_env, var, val); + rewritelog(r, 5, "setting env variable '%s' to '%s'", var, val); + } +} + +static void add_cookie(request_rec *r, char *s) +{ + char *var; + char *val; + char *domain; + char *expires; + char *path; + + char *tok_cntx; + char *cookie; + + if (s) { + var = apr_strtok(s, ":", &tok_cntx); + val = apr_strtok(NULL, ":", &tok_cntx); + domain = apr_strtok(NULL, ":", &tok_cntx); + /** the line below won't hit the token ever **/ + expires = apr_strtok(NULL, ":", &tok_cntx); + if (expires) { + path = apr_strtok(NULL,":", &tok_cntx); + } + else { + path = NULL; + } + + if (var && val && domain) { + /* FIX: use cached time similar to how logging does it */ + 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 == NULL) { + cookie = apr_pstrcat(rmain->pool, + var, "=", val, + "; path=", (path)? path : "/", + "; domain=", domain, + (expires)? "; expires=" : NULL, + (expires)? + ap_ht_time(r->pool, + r->request_time + + apr_time_from_sec((60 * + atol(expires))), + "%a, %d-%b-%Y %T GMT", 1) + : NULL, + NULL); + /* + * XXX: should we add it to err_headers_out as well ? + * if we do we need to be careful that only ONE gets sent out + */ + apr_table_add(rmain->err_headers_out, "Set-Cookie", cookie); + apr_pool_userdata_set("set", notename, NULL, rmain->pool); + rewritelog(rmain, 5, "setting cookie '%s'", cookie); + } + else { + rewritelog(rmain, 5, "skipping already set cookie '%s'", var); + } + } + } +} + + +/* +** +** check that a subrequest won't cause infinite recursion +** +*/ + +static int subreq_ok(request_rec *r) +{ + /* + * either not in a subrequest, or in a subrequest + * and URIs aren't NULL and sub/main URIs differ + */ + return (r->main == NULL + || (r->main->uri != NULL + && r->uri != NULL + && strcmp(r->main->uri, r->uri) != 0)); +} + + +/* +** +** stat() for only the prefix 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; +} + + +/* +** +** Lexicographic Compare +** +*/ + +static int compare_lexicography(char *cpNum1, char *cpNum2) +{ + int i; + int n1, n2; + + n1 = strlen(cpNum1); + n2 = strlen(cpNum2); + if (n1 > n2) { + return 1; + } + if (n1 < n2) { + return -1; + } + for (i = 0; i < n1; i++) { + if (cpNum1[i] > cpNum2[i]) { + return 1; + } + if (cpNum1[i] < cpNum2[i]) { + return -1; + } + } + return 0; +} + +/* +** +** Bracketed expression handling +** s points after the opening bracket +** +*/ + +static char *find_closing_bracket(char *s, int left, int right) +{ + int depth; + + for (depth = 1; *s; ++s) { + if (*s == right && --depth == 0) { + return s; + } + else if (*s == left) { + ++depth; + } + } + return NULL; +} + +static char *find_char_in_brackets(char *s, int c, int left, int right) +{ + int depth; + + for (depth = 1; *s; ++s) { + if (*s == c && depth == 1) { + return s; + } + else if (*s == right && --depth == 0) { + return NULL; + } + else if (*s == left) { + ++depth; + } + } + return NULL; +} + +/* +** +** Module paraphernalia +** +*/ + + /* the apr_table_t of commands we provide */ +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_TAKE2( "RewriteMap", cmd_rewritemap, NULL, RSRC_CONF, + "a mapname and a filename"), + AP_INIT_TAKE1( "RewriteLock", cmd_rewritelock, NULL, RSRC_CONF, + "the filename of a lockfile used for inter-process " + "synchronization"), + AP_INIT_TAKE1( "RewriteLog", cmd_rewritelog, NULL, RSRC_CONF, + "the filename of the rewriting logfile"), + AP_INIT_TAKE1( "RewriteLogLevel", cmd_rewriteloglevel, NULL, RSRC_CONF, + "the level of the rewriting logfile verbosity " + "(0=none, 1=std, .., 9=max)"), + { NULL } +}; + +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 }; + + /* check type before mod_mime, so that [T=foo/bar] will not be + * overridden by AddType definitions. + */ + static const char * const ct_aszSucc[]={ "mod_mime.c", NULL }; + + 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_translate_name(hook_uri2file, NULL, NULL, APR_HOOK_FIRST); + ap_hook_type_checker(hook_mimetype, NULL, ct_aszSucc, APR_HOOK_MIDDLE); +} + + /* the main config structure */ +module AP_MODULE_DECLARE_DATA rewrite_module = { + 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/rubbos/app/httpd-2.0.64/modules/mappers/mod_rewrite.dsp b/rubbos/app/httpd-2.0.64/modules/mappers/mod_rewrite.dsp new file mode 100644 index 00000000..9f8bee18 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_rewrite.dsp @@ -0,0 +1,128 @@ +# 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 /Zi /O2 /I "../../include" /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 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:"Release/mod_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 + +!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 "../../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 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_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 + +!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=.\mod_rewrite.rc +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\win32ver.awk + +!IF "$(CFG)" == "mod_rewrite - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_rewrite.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_rewrite.so "rewrite_module for Apache" ../../include/ap_release.h > .\mod_rewrite.rc + +# End Custom Build + +!ELSEIF "$(CFG)" == "mod_rewrite - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_rewrite.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_rewrite.so "rewrite_module for Apache" ../../include/ap_release.h > .\mod_rewrite.rc + +# End Custom Build + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_rewrite.exp b/rubbos/app/httpd-2.0.64/modules/mappers/mod_rewrite.exp new file mode 100644 index 00000000..8f2165bf --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_rewrite.exp @@ -0,0 +1 @@ +rewrite_module diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_rewrite.h b/rubbos/app/httpd-2.0.64/modules/mappers/mod_rewrite.h new file mode 100644 index 00000000..e648da98 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_rewrite.h @@ -0,0 +1,446 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MOD_REWRITE_H +#define MOD_REWRITE_H 1 + +/* +** _ _ _ +** _ __ ___ ___ __| | _ __ _____ ___ __(_) |_ ___ +** | '_ ` _ \ / _ \ / _` | | '__/ _ \ \ /\ / / '__| | __/ _ \ +** | | | | | | (_) | (_| | | | | __/\ 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" + +#define APR_WANT_STRFUNC +#define APR_WANT_MEMFUNC +#include "apr_want.h" + + /* Include from the underlaying Unix system ... */ +#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_SYS_TYPES_H +#include <sys/types.h> +#endif + +#if APR_HAS_THREADS +#include "apr_thread_mutex.h" +#endif +#include "apr_optional.h" +#include "apr_dbm.h" +#include "ap_config.h" + + /* Include from the Apache server ... */ +#define CORE_PRIVATE +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_log.h" +#include "http_vhost.h" + + /* + * The key in the r->notes apr_table_t wherein we store our accumulated + * Vary values, and the one used for per-condition checks in a chain. + */ +#define VARY_KEY "rewrite-Vary" +#define VARY_KEY_THIS "rewrite-Vary-this" + +/* +** +** Some defines +** +*/ + +#define ENVVAR_SCRIPT_URL "SCRIPT_URL" +#define ENVVAR_SCRIPT_URI "SCRIPT_URI" + +#define REWRITE_FORCED_MIMETYPE_NOTEVAR "rewrite-forced-mimetype" + +#define CONDFLAG_NONE 1<<0 +#define CONDFLAG_NOCASE 1<<1 +#define CONDFLAG_NOTMATCH 1<<2 +#define CONDFLAG_ORNEXT 1<<3 + +#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_FORBIDDEN 1<<9 +#define RULEFLAG_GONE 1<<10 +#define RULEFLAG_QSAPPEND 1<<11 +#define RULEFLAG_NOCASE 1<<12 +#define RULEFLAG_NOESCAPE 1<<13 + +#define ACTION_NORMAL 1<<0 +#define ACTION_NOESCAPE 1<<1 + +#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 ENGINE_DISABLED 1<<0 +#define ENGINE_ENABLED 1<<1 + +#define OPTION_NONE 1<<0 +#define OPTION_INHERIT 1<<1 + +#define CACHEMODE_TS 1<<0 +#define CACHEMODE_TTL 1<<1 + +#define CACHE_TLB_ROWS 1024 +#define CACHE_TLB_COLS 4 + +#ifndef FALSE +#define FALSE 0 +#define TRUE !FALSE +#endif + +#ifndef NO +#define NO FALSE +#define YES TRUE +#endif + +#ifndef RAND_MAX +#define RAND_MAX 32767 +#endif + +#ifndef LONG_STRING_LEN +#define LONG_STRING_LEN 2048 +#endif + +#define MAX_ENV_FLAGS 15 +#define MAX_COOKIE_FLAGS 15 +/*** max cookie size in rfc 2109 ***/ +#define MAX_COOKIE_LEN 4096 + +/* default maximum number of internal redirects */ +#define REWRITE_REDIRECT_LIMIT 10 + + +/* +** +** our private data structures we handle with +** +*/ + + /* the list structures for holding the mapfile information + * and the rewrite rules + */ +typedef struct { + const char *name; /* the name of the map */ + 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 */ + 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; + char *cachename; /* name for the cache */ +} rewritemap_entry; + +typedef struct { + char *input; /* Input string of RewriteCond */ + char *pattern; /* the RegExp pattern string */ + regex_t *regexp; + int flags; /* Flags which control the match */ +} rewritecond_entry; + +typedef struct { + apr_array_header_t *rewriteconds; /* the corresponding RewriteCond entries */ + char *pattern; /* the RegExp pattern string */ + 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 */ + int forced_responsecode; /* forced HTTP redirect response status */ + char *env[MAX_ENV_FLAGS+1]; /* added environment variables */ + char *cookie[MAX_COOKIE_FLAGS+1]; /* added cookies */ + int skip; /* number of next rules to skip */ +} rewriterule_entry; + + + /* the per-server or per-virtual-server configuration + * statically generated once on startup for every server + */ +typedef struct { + int state; /* the RewriteEngine state */ + int options; /* the RewriteOption state */ + const char *rewritelogfile; /* the RewriteLog filename */ + apr_file_t *rewritelogfp; /* the RewriteLog open filepointer */ + int rewriteloglevel; /* the RewriteLog level of verbosity */ + apr_array_header_t *rewritemaps; /* the RewriteMap entries */ + apr_array_header_t *rewriteconds; /* the RewriteCond entries (temporary) */ + apr_array_header_t *rewriterules; /* the RewriteRule entries */ + server_rec *server; /* the corresponding server indicator */ + int redirect_limit; /* maximum number of internal redirects */ +} rewrite_server_conf; + + + /* the per-directory configuration + * generated on-the-fly by Apache server for current request + */ +typedef struct { + int state; /* the RewriteEngine state */ + int options; /* the RewriteOption state */ + apr_array_header_t *rewriteconds; /* the RewriteCond entries (temporary) */ + apr_array_header_t *rewriterules; /* the RewriteRule entries */ + char *directory; /* the directory where it applies */ + const char *baseurl; /* the base-URL where it applies */ + int redirect_limit; /* maximum number of internal redirects */ +} rewrite_perdir_conf; + + + /* the per-request configuration + */ +typedef struct { + int redirects; /* current number of redirects */ + int redirect_limit; /* maximum number of redirects */ +} rewrite_request_conf; + + + /* the cache structures, + * a 4-way hash apr_table_t with LRU functionality + */ +typedef struct cacheentry { + apr_time_t time; + char *key; + char *value; +} cacheentry; + +typedef struct tlbentry { + int t[CACHE_TLB_COLS]; +} cachetlbentry; + +typedef struct cachelist { + char *resource; + apr_array_header_t *entries; + apr_array_header_t *tlb; +} cachelist; + +typedef struct cache { + apr_pool_t *pool; + apr_array_header_t *lists; +#if APR_HAS_THREADS + apr_thread_mutex_t *lock; +#endif +} cache; + + + /* the regex structure for the + * substitution of backreferences + */ +typedef struct backrefinfo { + char *source; + int nsub; + regmatch_t regmatch[AP_MAX_REG_MATCH]; +} backrefinfo; + + +/* +** +** forward declarations +** +*/ + + /* config structure handling */ +static void *config_server_create(apr_pool_t *p, server_rec *s); +static void *config_server_merge (apr_pool_t *p, void *basev, void *overridesv); +static void *config_perdir_create(apr_pool_t *p, char *path); +static void *config_perdir_merge (apr_pool_t *p, void *basev, void *overridesv); + + /* config directive handling */ +static const char *cmd_rewriteengine(cmd_parms *cmd, + void *dconf, int flag); +static const char *cmd_rewriteoptions(cmd_parms *cmd, + void *dconf, + const char *option); +static const char *cmd_rewritelog (cmd_parms *cmd, void *dconf, const char *a1); +static const char *cmd_rewriteloglevel(cmd_parms *cmd, void *dconf, const char *a1); +static const char *cmd_rewritemap (cmd_parms *cmd, void *dconf, + const char *a1, const char *a2); +static const char *cmd_rewritelock(cmd_parms *cmd, void *dconf, const char *a1); +static const char *cmd_rewritebase(cmd_parms *cmd, void *dconf, + const char *a1); +static const char *cmd_rewritecond(cmd_parms *cmd, void *dconf, + const char *str); +static const char *cmd_rewritecond_parseflagfield(apr_pool_t *p, + rewritecond_entry *new, + char *str); +static const char *cmd_rewritecond_setflag(apr_pool_t *p, rewritecond_entry *cfg, + char *key, char *val); +static const char *cmd_rewriterule(cmd_parms *cmd, void *dconf, + const char *str); +static const char *cmd_rewriterule_parseflagfield(apr_pool_t *p, + rewriterule_entry *new, + char *str); +static const char *cmd_rewriterule_setflag(apr_pool_t *p, rewriterule_entry *cfg, + char *key, char *val); + + /* initialisation */ +static int pre_config(apr_pool_t *pconf, + apr_pool_t *plog, + apr_pool_t *ptemp); +static int post_config(apr_pool_t *pconf, + apr_pool_t *plog, + apr_pool_t *ptemp, + server_rec *s); +static void init_child(apr_pool_t *p, server_rec *s); + + /* runtime hooks */ +static int hook_uri2file (request_rec *r); +static int hook_mimetype (request_rec *r); +static int hook_fixup (request_rec *r); +static int handler_redirect(request_rec *r); + + /* rewriting engine */ +static int apply_rewrite_list(request_rec *r, apr_array_header_t *rewriterules, + char *perdir); +static int apply_rewrite_rule(request_rec *r, rewriterule_entry *p, + char *perdir); +static int apply_rewrite_cond(request_rec *r, rewritecond_entry *p, + char *perdir, backrefinfo *briRR, + backrefinfo *briRC); + +static void do_expand(request_rec *r, char *input, char *buffer, int nbuf, + backrefinfo *briRR, backrefinfo *briRC); +static void do_expand_env(request_rec *r, char *env[], + backrefinfo *briRR, backrefinfo *briRC); +static void do_expand_cookie(request_rec *r, char *cookie[], + backrefinfo *briRR, backrefinfo *briRC); + + /* URI transformation function */ +static void splitout_queryargs(request_rec *r, int qsappend); +static void fully_qualify_uri(request_rec *r); +static void reduce_uri(request_rec *r); +static unsigned is_absolute_uri(char *uri); +static char *escape_absolute_uri(apr_pool_t *p, char *uri, unsigned scheme); +static char *expand_tildepaths(request_rec *r, char *uri); + + /* rewrite map support functions */ +static char *lookup_map(request_rec *r, char *name, char *key); +static char *lookup_map_txtfile(request_rec *r, const char *file, char *key); +static char *lookup_map_dbmfile(request_rec *r, const char *file, + const char *dbmtype, char *key); +static char *lookup_map_program(request_rec *r, apr_file_t *fpin, + apr_file_t *fpout, char *key); + +typedef char *(rewrite_mapfunc_t)(request_rec *r, char *key); +static void ap_register_rewrite_mapfunc(char *name, rewrite_mapfunc_t *func); +APR_DECLARE_OPTIONAL_FN(void, ap_register_rewrite_mapfunc, + (char *name, rewrite_mapfunc_t *func)); + +static char *rewrite_mapfunc_toupper(request_rec *r, char *key); +static char *rewrite_mapfunc_tolower(request_rec *r, char *key); +static char *rewrite_mapfunc_escape(request_rec *r, char *key); +static char *rewrite_mapfunc_unescape(request_rec *r, char *key); + +static char *select_random_value_part(request_rec *r, char *value); +static void rewrite_rand_init(void); +static int rewrite_rand(int l, int h); + + /* rewriting logfile support */ +static int open_rewritelog(server_rec *s, apr_pool_t *p); +static void rewritelog(request_rec *r, int level, const char *text, ...) + __attribute__((format(printf,3,4))); +static char *current_logtime(request_rec *r); + + /* rewriting lockfile support */ +static apr_status_t rewritelock_create(server_rec *s, apr_pool_t *p); +static apr_status_t rewritelock_remove(void *data); + + /* program map support */ +static apr_status_t run_rewritemap_programs(server_rec *s, apr_pool_t *p); +static apr_status_t rewritemap_program_child(apr_pool_t *p, + const char *progname, char **argv, + apr_file_t **fpout, + apr_file_t **fpin); + + /* env variable support */ +static char *lookup_variable(request_rec *r, char *var); +static char *lookup_header(request_rec *r, const char *name); + + /* caching functions */ +static cache *init_cache(apr_pool_t *p); +static char *get_cache_string(cache *c, const char *res, int mode, apr_time_t mtime, + char *key); +static void set_cache_string(cache *c, const char *res, int mode, apr_time_t mtime, + char *key, char *value); +static cacheentry *retrieve_cache_string(cache *c, const char *res, char *key); +static void store_cache_string(cache *c, const char *res, cacheentry *ce); + + /* misc functions */ +static char *subst_prefix_path(request_rec *r, char *input, char *match, + const char *subst); +static int parseargline(char *str, char **a1, char **a2, char **a3); +static int prefix_stat(const char *path, apr_pool_t *pool); +static void add_env_variable(request_rec *r, char *s); +static void add_cookie(request_rec *r, char *s); +static int subreq_ok(request_rec *r); +static int is_redirect_limit_exceeded(request_rec *r); + + /* Lexicographic Comparison */ +static int compare_lexicography(char *cpNum1, char *cpNum2); + + /* Bracketed expression handling */ +static char *find_closing_bracket(char *s, int left, int right); +static char *find_char_in_brackets(char *s, int c, int left, int right); + +#endif /* MOD_REWRITE_H */ diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_so.c b/rubbos/app/httpd-2.0.64/modules/mappers/mod_so.c new file mode 100644 index 00000000..984cb549 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_so.c @@ -0,0 +1,368 @@ +/* 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 module is used to load Apache modules at runtime. This means that the + * server functionality can be extended without recompiling and even without + * taking the server down at all. Only a HUP or AP_SIG_GRACEFUL signal + * needs to be sent to the server to reload the dynamically loaded modules. + * + * To use, you'll first need to build your module as a shared library, then + * update your configuration (httpd.conf) to get the Apache core to load the + * module at start-up. + * + * The easiest way to build a module as a shared library is to use the + * `SharedModule' command in the Configuration file, instead of `AddModule'. + * You should also change the file extension from `.o' to `.so'. So, for + * example, to build the status module as a shared library edit Configuration + * and change + * AddModule modules/standard/mod_status.o + * to + * SharedModule modules/standard/mod_status.so + * + * Run Configure and make. Now Apache's httpd binary will _not_ include + * mod_status. Instead a shared object called mod_status.so will be build, in + * the modules/standard directory. You can build most of the modules as shared + * libraries like this. + * + * To use the shared module, move the .so file(s) into an appropriate + * directory. You might like to create a directory called "modules" under you + * server root for this (e.g. /usr/local/httpd/modules). + * + * Then edit your conf/httpd.conf file, and add LoadModule lines. For + * example + * LoadModule status_module modules/mod_status.so + * + * The first argument is the module's structure name (look at the end of the + * module source to find this). The second option is the path to the module + * file, relative to the server root. Put these directives right at the top + * of your httpd.conf file. + * + * Now you can start Apache. A message will be logged at "debug" level to your + * error_log to confirm that the module(s) are loaded (use "LogLevel debug" + * directive to get these log messages). + * + * If you edit the LoadModule directives while the server is live you can get + * Apache to re-load the modules by sending it a HUP or AP_SIG_GRACEFUL + * signal as normal. You can use this to dynamically change the capability + * of your server without bringing it down. + * + * Because currently there is only limited builtin support in the Configure + * script for creating the shared library files (`.so'), please consult your + * vendors cc(1), ld(1) and dlopen(3) manpages to find out the appropriate + * compiler and linker flags and insert them manually into the Configuration + * file under CFLAGS_SHLIB, LDFLAGS_SHLIB and LDFLAGS_SHLIB_EXPORT. + * + * If you still have problems figuring out the flags both try the paper + * http://developer.netscape.com/library/documentation/enterprise + * /unix/svrplug.htm#1013807 + * or install a Perl 5 interpreter on your platform and then run the command + * + * $ perl -V:usedl -V:ccdlflags -V:cccdlflags -V:lddlflags + * + * This gives you what type of dynamic loading Perl 5 uses on your platform + * and which compiler and linker flags Perl 5 uses to create the shared object + * files. + * + * Another location where you can find useful hints is the `ltconfig' script + * of the GNU libtool 1.2 package. Search for your platform name inside the + * various "case" constructs. + * + */ + +#include "apr.h" +#include "apr_dso.h" +#include "apr_strings.h" +#include "apr_errno.h" + +#define CORE_PRIVATE +#include "ap_config.h" +#include "httpd.h" +#include "http_config.h" +#include "http_log.h" +#include "ap_config.h" + +module AP_MODULE_DECLARE_DATA so_module; + + +/* + * Server configuration to keep track of actually + * loaded modules and the corresponding module name. + */ + +typedef struct moduleinfo { + const char *name; + module *modp; +} moduleinfo; + +typedef struct so_server_conf { + apr_array_header_t *loaded_modules; +} so_server_conf; + +static void *so_sconf_create(apr_pool_t *p, server_rec *s) +{ + so_server_conf *soc; + + soc = (so_server_conf *)apr_pcalloc(p, sizeof(so_server_conf)); + soc->loaded_modules = apr_array_make(p, DYNAMIC_MODULE_LIMIT, + sizeof(moduleinfo)); + + return (void *)soc; +} + +#ifndef NO_DLOPEN + +/* + * This is the cleanup for a loaded shared object. It unloads the module. + * This is called as a cleanup function from the core. + */ + +static apr_status_t unload_module(void *data) +{ + moduleinfo *modi = (moduleinfo*)data; + + /* only unload if module information is still existing */ + if (modi->modp == NULL) + return APR_SUCCESS; + + /* remove the module pointer from the core structure */ + ap_remove_loaded_module(modi->modp); + + /* destroy the module information */ + modi->modp = NULL; + modi->name = NULL; + return APR_SUCCESS; +} + +/* + * This is called for the directive LoadModule and actually loads + * a shared object file into the address space of the server process. + */ + +static const char *load_module(cmd_parms *cmd, void *dummy, + const char *modname, const char *filename) +{ + apr_dso_handle_t *modhandle; + apr_dso_handle_sym_t modsym; + module *modp; + const char *szModuleFile = ap_server_root_relative(cmd->pool, filename); + so_server_conf *sconf; + moduleinfo *modi; + moduleinfo *modie; + int i; + + /* we need to setup this value for dummy to make sure that we don't try + * to add a non-existant tree into the build when we return to + * execute_now. + */ + *(ap_directive_t **)dummy = NULL; + + if (!szModuleFile) { + return apr_pstrcat(cmd->pool, "Invalid LoadModule path ", + filename, NULL); + } + + /* + * check for already existing module + * If it already exists, we have nothing to do + * Check both dynamically-loaded modules and statically-linked modules. + */ + sconf = (so_server_conf *)ap_get_module_config(cmd->server->module_config, + &so_module); + modie = (moduleinfo *)sconf->loaded_modules->elts; + for (i = 0; i < sconf->loaded_modules->nelts; i++) { + modi = &modie[i]; + if (modi->name != NULL && strcmp(modi->name, modname) == 0) { + ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, + cmd->pool, "module %s is already loaded, skipping", + modname); + return NULL; + } + } + + for (i = 0; ap_preloaded_modules[i]; i++) { + const char *preload_name; + apr_size_t preload_len; + apr_size_t thismod_len; + + modp = ap_preloaded_modules[i]; + + /* make sure we're comparing apples with apples + * make sure name of preloaded module is mod_FOO.c + * make sure name of structure being loaded is FOO_module + */ + + if (memcmp(modp->name, "mod_", 4)) { + continue; + } + + preload_name = modp->name + strlen("mod_"); + preload_len = strlen(preload_name) - 2; + + if (strlen(modname) <= strlen("_module")) { + continue; + } + thismod_len = strlen(modname) - strlen("_module"); + if (strcmp(modname + thismod_len, "_module")) { + continue; + } + + if (thismod_len != preload_len) { + continue; + } + + if (!memcmp(modname, preload_name, preload_len)) { + return apr_pstrcat(cmd->pool, "module ", modname, + " is built-in and can't be loaded", + NULL); + } + } + + modi = apr_array_push(sconf->loaded_modules); + modi->name = modname; + + /* + * Load the file into the Apache address space + */ + if (apr_dso_load(&modhandle, szModuleFile, cmd->pool) != APR_SUCCESS) { + char my_error[256]; + + return apr_pstrcat(cmd->pool, "Cannot load ", szModuleFile, + " into server: ", + apr_dso_error(modhandle, my_error, sizeof(my_error)), + NULL); + } + ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, cmd->pool, + "loaded module %s", modname); + + /* + * Retrieve the pointer to the module structure through the module name: + * First with the hidden variant (prefix `AP_') and then with the plain + * symbol name. + */ + if (apr_dso_sym(&modsym, modhandle, modname) != APR_SUCCESS) { + char my_error[256]; + + return apr_pstrcat(cmd->pool, "Can't locate API module structure `", + modname, "' in file ", szModuleFile, ": ", + apr_dso_error(modhandle, my_error, sizeof(my_error)), + NULL); + } + modp = (module*) modsym; + modp->dynamic_load_handle = (apr_dso_handle_t *)modhandle; + modi->modp = modp; + + /* + * Make sure the found module structure is really a module structure + * + */ + if (modp->magic != MODULE_MAGIC_COOKIE) { + return apr_psprintf(cmd->pool, "API module structure '%s' in file %s " + "is garbled - expected signature %08lx but saw " + "%08lx - perhaps this is not an Apache module DSO, " + "or was compiled for a different Apache version?", + modname, szModuleFile, + MODULE_MAGIC_COOKIE, modp->magic); + } + + /* + * Add this module to the Apache core structures + */ + ap_add_loaded_module(modp, cmd->pool); + + /* + * Register a cleanup in the config apr_pool_t (normally pconf). When + * we do a restart (or shutdown) this cleanup will cause the + * shared object to be unloaded. + */ + apr_pool_cleanup_register(cmd->pool, modi, unload_module, apr_pool_cleanup_null); + + /* + * Finally we need to run the configuration process for the module + */ + ap_single_module_configure(cmd->pool, cmd->server, modp); + + return NULL; +} + +/* + * This implements the LoadFile directive and loads an arbitrary + * shared object file into the adress space of the server process. + */ + +static const char *load_file(cmd_parms *cmd, void *dummy, const char *filename) +{ + apr_dso_handle_t *handle; + const char *file; + + file = ap_server_root_relative(cmd->pool, filename); + + if (!file) { + return apr_pstrcat(cmd->pool, "Invalid LoadFile path ", + filename, NULL); + } + + if (apr_dso_load(&handle, file, cmd->pool) != APR_SUCCESS) { + char my_error[256]; + + return apr_pstrcat(cmd->pool, "Cannot load ", filename, + " into server: ", + apr_dso_error(handle, my_error, sizeof(my_error)), + NULL); + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, + "loaded file %s", filename); + + return NULL; +} + +#else /* not NO_DLOPEN */ + +static const char *load_file(cmd_parms *cmd, void *dummy, const char *filename) +{ + ap_log_perror(APLOG_MARK, APLOG_STARTUP, 0, cmd->pool, + "WARNING: LoadFile not supported on this platform"); + return NULL; +} + +static const char *load_module(cmd_parms *cmd, void *dummy, + const char *modname, const char *filename) +{ + ap_log_perror(APLOG_MARK, APLOG_STARTUP, 0, cmd->pool, + "WARNING: LoadModule not supported on this platform"); + return NULL; +} + +#endif /* NO_DLOPEN */ + +static const command_rec so_cmds[] = { + AP_INIT_TAKE2("LoadModule", load_module, NULL, RSRC_CONF | EXEC_ON_READ, + "a module name and the name of a shared object file to load it from"), + AP_INIT_ITERATE("LoadFile", load_file, NULL, RSRC_CONF | EXEC_ON_READ, + "shared object file or library to load into the server at runtime"), + { NULL } +}; + +module AP_MODULE_DECLARE_DATA so_module = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-dir config */ + NULL, /* merge per-dir config */ + so_sconf_create, /* server config */ + NULL, /* merge server config */ + so_cmds, /* command apr_table_t */ + NULL /* register hooks */ +}; diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_so.la b/rubbos/app/httpd-2.0.64/modules/mappers/mod_so.la new file mode 100644 index 00000000..1c9e8489 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_so.la @@ -0,0 +1,35 @@ +# mod_so.la - a libtool library file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='' + +# Names of this library. +library_names='' + +# The name of the static archive. +old_library='mod_so.a' + +# Libraries that this one depends upon. +dependency_libs=' -L/bottlenecks/rubbos/app/httpd-2.0.64/srclib/apr-util/xml/expat/lib' + +# Version information for mod_so. +current= +age= +revision= + +# Is this an already installed library? +installed=no + +# Should we warn about portability when linking against -modules? +shouldnotlink=yes + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='' diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_so.lo b/rubbos/app/httpd-2.0.64/modules/mappers/mod_so.lo new file mode 100644 index 00000000..917d7775 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_so.lo @@ -0,0 +1,12 @@ +# mod_so.lo - a libtool object file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object='.libs/mod_so.o' + +# Name of the non-PIC object. +non_pic_object='mod_so.o' + diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_so.o b/rubbos/app/httpd-2.0.64/modules/mappers/mod_so.o Binary files differnew file mode 100644 index 00000000..71d442fa --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_so.o diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_speling.c b/rubbos/app/httpd-2.0.64/modules/mappers/mod_speling.c new file mode 100644 index 00000000..9520cbee --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_speling.c @@ -0,0 +1,532 @@ +/* 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" + +#define WANT_BASENAME_MATCH + +#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. + * + * 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; +} 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; + 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); +} + +/* + * Handler for the CheckSpelling directive, which is FLAG. + */ +static const char *set_speling(cmd_parms *cmd, void *mconfig, int arg) +{ + spconfig *cfg = (spconfig *) mconfig; + + cfg->enabled = arg; + return NULL; +} + +/* + * 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", set_speling, NULL, OR_OPTIONS, + "whether or not to fix miscapitalized/misspelled requests"), + { 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 != 0) { + 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 ((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). + * I would propose to not set the WANT_BASENAME_MATCH define. + * 08-Aug-1997 <Martin.Kraemer@Mch.SNI.De> + * + * However, Alexei replied giving some reasons to add it anyway: + * > Oh, by the way, I remembered why having the + * > extension-stripping-and-matching stuff is a good idea: + * > + * > If you're using MultiViews, and have a file named foobar.html, + * > which you refer to as "foobar", and someone tried to access + * > "Foobar", mod_speling won't find it, because it won't find + * > anything matching that spelling. With the extension-munging, + * > it would locate "foobar.html". Not perfect, but I ran into + * > that problem when I first wrote the module. + */ + else { +#ifdef WANT_BASENAME_MATCH + /* + * 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; + } +#endif + } + } + 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 ? "Fixed spelling: %s to %s from %s" + : "Fixed spelling: %s to %s", + r->uri, nuri, 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; + + 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 " + "<a href=\""; + *(const char **)apr_array_push(t) = ap_escape_uri(sub_pool, ref); + *(const char **)apr_array_push(t) = "\">referring page</a> " + "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 ? "Spelling fix: %s: %d candidates from %s" + : "Spelling fix: %s: %d candidates", + r->uri, candidates->nelts, 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); +} + +module AP_MODULE_DECLARE_DATA speling_module = +{ + 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/rubbos/app/httpd-2.0.64/modules/mappers/mod_speling.dsp b/rubbos/app/httpd-2.0.64/modules/mappers/mod_speling.dsp new file mode 100644 index 00000000..46626b0c --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_speling.dsp @@ -0,0 +1,128 @@ +# 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 /Zi /O2 /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 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:"Release/mod_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 + +!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 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_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 + +!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=.\mod_speling.rc +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\win32ver.awk + +!IF "$(CFG)" == "mod_speling - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_speling.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_speling.so "speling_module for Apache" ../../include/ap_release.h > .\mod_speling.rc + +# End Custom Build + +!ELSEIF "$(CFG)" == "mod_speling - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_speling.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_speling.so "speling_module for Apache" ../../include/ap_release.h > .\mod_speling.rc + +# End Custom Build + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_speling.exp b/rubbos/app/httpd-2.0.64/modules/mappers/mod_speling.exp new file mode 100644 index 00000000..a6ee8b50 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_speling.exp @@ -0,0 +1 @@ +speling_module diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_userdir.c b/rubbos/app/httpd-2.0.64/modules/mappers/mod_userdir.c new file mode 100644 index 00000000..b071b1a7 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_userdir.c @@ -0,0 +1,366 @@ +/* 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(BEOS) && !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 */ +#ifndef DEFAULT_USER_DIR +#define DEFAULT_USER_DIR "public_html" +#endif + +module AP_MODULE_DECLARE_DATA userdir_module; + +typedef struct { + int globally_disabled; + 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 = 0; + newcfg->userdir = DEFAULT_USER_DIR; + newcfg->enabled_users = apr_table_make(p, 4); + newcfg->disabled_users = apr_table_make(p, 4); + + return newcfg; +} + +#define O_DEFAULT 0 +#define O_ENABLE 1 +#define O_DISABLE 2 + +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->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 (strlen(usernames) == 0) { + s_cfg->globally_disabled = 1; + return NULL; + } + usertable = s_cfg->disabled_users; + } + else if ((!strcasecmp(kw, "enable")) || (!strcasecmp(kw, "enabled"))) { + /* + * The "disable" keyword can stand alone or take a list of names, but + * the "enable" keyword requires the list. Whinge if it doesn't have + * it. + */ + if (strlen(usernames) == 0) { + return "UserDir \"enable\" keyword requires a list of usernames"; + } + 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 = apr_pstrdup(cmd->pool, 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, kw); + } + 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; + char *name = r->uri; + const char *userdirs; + const char *w, *dname; + char *redirect; + apr_finfo_t statbuf; + + /* + * If the URI doesn't match our basic pattern, we've nothing to do with + * it. + */ + if (name[0] != '/' || name[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 = name + 2; + w = 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 (w[0] == '\0' || (w[1] == '.' && (w[2] == '\0' || (w[2] == '.' && w[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, w) != 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 + && apr_table_get(s_cfg->enabled_users, w) == 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, *x = NULL; + apr_status_t rv; + int is_absolute = ap_os_is_path_absolute(r->pool, userdir); + + if (ap_strchr_c(userdir, '*')) + x = ap_getword(r->pool, &userdir, '*'); + + if (userdir[0] == '\0' || is_absolute) { + if (x) { +#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(x + 2, ':')) +#else + if (strchr(x, ':') && !is_absolute) +#endif /* HAVE_DRIVE_LETTERS */ + { + redirect = apr_pstrcat(r->pool, x, w, userdir, dname, NULL); + apr_table_setn(r->headers_out, "Location", redirect); + return HTTP_MOVED_TEMPORARILY; + } + else + filename = apr_pstrcat(r->pool, x, w, userdir, NULL); + } + else + filename = apr_pstrcat(r->pool, userdir, "/", w, NULL); + } + else if (x && ap_strchr_c(x, ':')) { + redirect = apr_pstrcat(r->pool, x, w, dname, NULL); + apr_table_setn(r->headers_out, "Location", redirect); + return HTTP_MOVED_TEMPORARILY; + } + else { +#if APR_HAS_USER + char *homedir; + + if (apr_get_home_directory(&homedir, w, 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); + /* 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", w); + + 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_get_userid(&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 +} + +module AP_MODULE_DECLARE_DATA userdir_module = { + STANDARD20_MODULE_STUFF, + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + create_userdir_config, /* server config */ + NULL, /* merge server config */ + userdir_cmds, /* command apr_table_t */ + register_hooks /* register hooks */ +}; diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_userdir.dsp b/rubbos/app/httpd-2.0.64/modules/mappers/mod_userdir.dsp new file mode 100644 index 00000000..04c078b4 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_userdir.dsp @@ -0,0 +1,128 @@ +# 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 /Zi /O2 /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 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:"Release/mod_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 + +!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 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_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 + +!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=.\mod_userdir.rc +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\win32ver.awk + +!IF "$(CFG)" == "mod_userdir - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_userdir.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_userdir.so "userdir_module for Apache" ../../include/ap_release.h > .\mod_userdir.rc + +# End Custom Build + +!ELSEIF "$(CFG)" == "mod_userdir - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_userdir.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_userdir.so "userdir_module for Apache" ../../include/ap_release.h > .\mod_userdir.rc + +# End Custom Build + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_userdir.exp b/rubbos/app/httpd-2.0.64/modules/mappers/mod_userdir.exp new file mode 100644 index 00000000..6b8b81d5 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_userdir.exp @@ -0,0 +1 @@ +userdir_module diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_userdir.la b/rubbos/app/httpd-2.0.64/modules/mappers/mod_userdir.la new file mode 100644 index 00000000..2f7eeb0b --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_userdir.la @@ -0,0 +1,35 @@ +# mod_userdir.la - a libtool library file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='' + +# Names of this library. +library_names='' + +# The name of the static archive. +old_library='mod_userdir.a' + +# Libraries that this one depends upon. +dependency_libs=' -L/bottlenecks/rubbos/app/httpd-2.0.64/srclib/apr-util/xml/expat/lib' + +# Version information for mod_userdir. +current= +age= +revision= + +# Is this an already installed library? +installed=no + +# Should we warn about portability when linking against -modules? +shouldnotlink=yes + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='' diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_userdir.lo b/rubbos/app/httpd-2.0.64/modules/mappers/mod_userdir.lo new file mode 100644 index 00000000..445d354f --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_userdir.lo @@ -0,0 +1,12 @@ +# mod_userdir.lo - a libtool object file +# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.493 2008/02/01 16:58:18) +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object='.libs/mod_userdir.o' + +# Name of the non-PIC object. +non_pic_object='mod_userdir.o' + diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_userdir.o b/rubbos/app/httpd-2.0.64/modules/mappers/mod_userdir.o Binary files differnew file mode 100644 index 00000000..677c39d1 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_userdir.o diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_vhost_alias.c b/rubbos/app/httpd-2.0.64/modules/mappers/mod_vhost_alias.c new file mode 100644 index 00000000..cb798863 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/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 "apr_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 (strcasecmp(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, last; + + 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; + last = '\0'; + while (*map) { + if (*map != '%') { + /* normal characters */ + vhost_alias_checkspace(r, buf, &dest, 1); + last = *dest++ = *map++; + continue; + } + /* we are in a format specifier */ + ++map; + /* can't be a slash */ + last = '\0'; + /* %% -> % */ + 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 */ + N = 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); + } + } + *dest = '\0'; + /* no double slashes */ + if (last == '/') { + ++uri; + } + + if (r->filename) { + r->filename = apr_pstrcat(r->pool, r->filename, buf, uri, NULL); + } + else { + r->filename = apr_pstrcat(r->pool, buf, uri, NULL); + } +} + +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); + } + + 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); +} + +module AP_MODULE_DECLARE_DATA vhost_alias_module = +{ + 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/rubbos/app/httpd-2.0.64/modules/mappers/mod_vhost_alias.dsp b/rubbos/app/httpd-2.0.64/modules/mappers/mod_vhost_alias.dsp new file mode 100644 index 00000000..3b322e23 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_vhost_alias.dsp @@ -0,0 +1,128 @@ +# 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 /Zi /O2 /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 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:"Release/mod_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 + +!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 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_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 + +!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=.\mod_vhost_alias.rc +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\win32ver.awk + +!IF "$(CFG)" == "mod_vhost_alias - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_vhost_alias.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_vhost_alias.so "vhost_alias_module for Apache" ../../include/ap_release.h > .\mod_vhost_alias.rc + +# End Custom Build + +!ELSEIF "$(CFG)" == "mod_vhost_alias - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_vhost_alias.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_vhost_alias.so "vhost_alias_module for Apache" ../../include/ap_release.h > .\mod_vhost_alias.rc + +# End Custom Build + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/mod_vhost_alias.exp b/rubbos/app/httpd-2.0.64/modules/mappers/mod_vhost_alias.exp new file mode 100644 index 00000000..b17666fc --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/mod_vhost_alias.exp @@ -0,0 +1 @@ +vhost_alias_module diff --git a/rubbos/app/httpd-2.0.64/modules/mappers/modules.mk b/rubbos/app/httpd-2.0.64/modules/mappers/modules.mk new file mode 100644 index 00000000..8fb09eec --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/mappers/modules.mk @@ -0,0 +1,17 @@ +mod_negotiation.la: mod_negotiation.lo + $(MOD_LINK) mod_negotiation.lo $(MOD_NEGOTIATION_LDADD) +mod_dir.la: mod_dir.lo + $(MOD_LINK) mod_dir.lo $(MOD_DIR_LDADD) +mod_imap.la: mod_imap.lo + $(MOD_LINK) mod_imap.lo $(MOD_IMAP_LDADD) +mod_actions.la: mod_actions.lo + $(MOD_LINK) mod_actions.lo $(MOD_ACTIONS_LDADD) +mod_userdir.la: mod_userdir.lo + $(MOD_LINK) mod_userdir.lo $(MOD_USERDIR_LDADD) +mod_alias.la: mod_alias.lo + $(MOD_LINK) mod_alias.lo $(MOD_ALIAS_LDADD) +mod_so.la: mod_so.lo + $(MOD_LINK) mod_so.lo $(MOD_SO_LDADD) +DISTCLEAN_TARGETS = modules.mk +static = mod_negotiation.la mod_dir.la mod_imap.la mod_actions.la mod_userdir.la mod_alias.la mod_so.la +shared = |