From 5dff2d61cc1c27747ee398e04d8e02843aabb1f8 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Tue, 7 May 2024 04:04:06 +0200 Subject: Adding upstream version 2.4.38. Signed-off-by: Daniel Baumann --- modules/generators/.indent.pro | 54 + modules/generators/Makefile.in | 3 + modules/generators/NWGNUautoindex | 249 ++++ modules/generators/NWGNUinfo | 248 ++++ modules/generators/NWGNUmakefile | 249 ++++ modules/generators/NWGNUmod_asis | 249 ++++ modules/generators/NWGNUmod_cgi | 249 ++++ modules/generators/NWGNUstatus | 248 ++++ modules/generators/config5.m4 | 81 ++ modules/generators/mod_asis.c | 128 ++ modules/generators/mod_asis.dep | 56 + modules/generators/mod_asis.dsp | 111 ++ modules/generators/mod_asis.exp | 1 + modules/generators/mod_asis.mak | 353 +++++ modules/generators/mod_autoindex.c | 2348 ++++++++++++++++++++++++++++++++++ modules/generators/mod_autoindex.dep | 61 + modules/generators/mod_autoindex.dsp | 111 ++ modules/generators/mod_autoindex.exp | 1 + modules/generators/mod_autoindex.mak | 353 +++++ modules/generators/mod_cgi.c | 1281 +++++++++++++++++++ modules/generators/mod_cgi.dep | 64 + modules/generators/mod_cgi.dsp | 115 ++ modules/generators/mod_cgi.exp | 1 + modules/generators/mod_cgi.h | 67 + modules/generators/mod_cgi.mak | 353 +++++ modules/generators/mod_cgid.c | 1980 ++++++++++++++++++++++++++++ modules/generators/mod_cgid.exp | 1 + modules/generators/mod_info.c | 1011 +++++++++++++++ modules/generators/mod_info.dep | 66 + modules/generators/mod_info.dsp | 111 ++ modules/generators/mod_info.exp | 1 + modules/generators/mod_info.mak | 353 +++++ modules/generators/mod_status.c | 1051 +++++++++++++++ modules/generators/mod_status.dep | 60 + modules/generators/mod_status.dsp | 111 ++ modules/generators/mod_status.exp | 1 + modules/generators/mod_status.h | 64 + modules/generators/mod_status.mak | 353 +++++ modules/generators/mod_suexec.c | 139 ++ modules/generators/mod_suexec.h | 33 + 40 files changed, 12369 insertions(+) create mode 100644 modules/generators/.indent.pro create mode 100644 modules/generators/Makefile.in create mode 100644 modules/generators/NWGNUautoindex create mode 100644 modules/generators/NWGNUinfo create mode 100644 modules/generators/NWGNUmakefile create mode 100644 modules/generators/NWGNUmod_asis create mode 100644 modules/generators/NWGNUmod_cgi create mode 100644 modules/generators/NWGNUstatus create mode 100644 modules/generators/config5.m4 create mode 100644 modules/generators/mod_asis.c create mode 100644 modules/generators/mod_asis.dep create mode 100644 modules/generators/mod_asis.dsp create mode 100644 modules/generators/mod_asis.exp create mode 100644 modules/generators/mod_asis.mak create mode 100644 modules/generators/mod_autoindex.c create mode 100644 modules/generators/mod_autoindex.dep create mode 100644 modules/generators/mod_autoindex.dsp create mode 100644 modules/generators/mod_autoindex.exp create mode 100644 modules/generators/mod_autoindex.mak create mode 100644 modules/generators/mod_cgi.c create mode 100644 modules/generators/mod_cgi.dep create mode 100644 modules/generators/mod_cgi.dsp create mode 100644 modules/generators/mod_cgi.exp create mode 100644 modules/generators/mod_cgi.h create mode 100644 modules/generators/mod_cgi.mak create mode 100644 modules/generators/mod_cgid.c create mode 100644 modules/generators/mod_cgid.exp create mode 100644 modules/generators/mod_info.c create mode 100644 modules/generators/mod_info.dep create mode 100644 modules/generators/mod_info.dsp create mode 100644 modules/generators/mod_info.exp create mode 100644 modules/generators/mod_info.mak create mode 100644 modules/generators/mod_status.c create mode 100644 modules/generators/mod_status.dep create mode 100644 modules/generators/mod_status.dsp create mode 100644 modules/generators/mod_status.exp create mode 100644 modules/generators/mod_status.h create mode 100644 modules/generators/mod_status.mak create mode 100644 modules/generators/mod_suexec.c create mode 100644 modules/generators/mod_suexec.h (limited to 'modules/generators') diff --git a/modules/generators/.indent.pro b/modules/generators/.indent.pro new file mode 100644 index 0000000..a9fbe9f --- /dev/null +++ b/modules/generators/.indent.pro @@ -0,0 +1,54 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-Tbuf_area +-Tbuff_struct +-Tbuffy +-Tcmd_how +-Tcmd_parms +-Tcommand_rec +-Tcommand_struct +-Tconn_rec +-Tcore_dir_config +-Tcore_server_config +-Tdir_maker_func +-Tevent +-Tglobals_s +-Thandler_func +-Thandler_rec +-Tjoblist_s +-Tlisten_rec +-Tmerger_func +-Tmode_t +-Tmodule +-Tmodule_struct +-Tmutex +-Tn_long +-Tother_child_rec +-Toverrides_t +-Tparent_score +-Tpid_t +-Tpiped_log +-Tpool +-Trequest_rec +-Trequire_line +-Trlim_t +-Tscoreboard +-Tsemaphore +-Tserver_addr_rec +-Tserver_rec +-Tserver_rec_chain +-Tshort_score +-Ttable +-Ttable_entry +-Tthread +-Tu_wide_int +-Tvtime_t +-Twide_int diff --git a/modules/generators/Makefile.in b/modules/generators/Makefile.in new file mode 100644 index 0000000..167b343 --- /dev/null +++ b/modules/generators/Makefile.in @@ -0,0 +1,3 @@ + +include $(top_srcdir)/build/special.mk + diff --git a/modules/generators/NWGNUautoindex b/modules/generators/NWGNUautoindex new file mode 100644 index 0000000..e8080c3 --- /dev/null +++ b/modules/generators/NWGNUautoindex @@ -0,0 +1,249 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)/build/NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(AP_WORK)/modules/http \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = autoindex + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Autoindex Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Autoindex Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/autoindex.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_autoindex.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @aprlib.imp \ + @httpd.imp \ + @libc.imp \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + autoindex_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/generators/NWGNUinfo b/modules/generators/NWGNUinfo new file mode 100644 index 0000000..810db8a --- /dev/null +++ b/modules/generators/NWGNUinfo @@ -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 = info + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Info Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Info Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/info.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_info.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @aprlib.imp \ + @httpd.imp \ + @libc.imp \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + info_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/generators/NWGNUmakefile b/modules/generators/NWGNUmakefile new file mode 100644 index 0000000..df9cfec --- /dev/null +++ b/modules/generators/NWGNUmakefile @@ -0,0 +1,249 @@ +# +# 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)/mod_asis.nlm \ + $(OBJDIR)/autoindex.nlm \ + $(OBJDIR)/mod_cgi.nlm \ + $(OBJDIR)/info.nlm \ + $(OBJDIR)/status.nlm \ + $(EOLIST) + +# +# If there is an LIB target, put it here +# +TARGET_lib = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + $(call COPY,$(OBJDIR)/*.nlm, $(INSTALLBASE)/modules/) + +# +# Any specialized rules here +# + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/generators/NWGNUmod_asis b/modules/generators/NWGNUmod_asis new file mode 100644 index 0000000..c7df5ae --- /dev/null +++ b/modules/generators/NWGNUmod_asis @@ -0,0 +1,249 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)/build/NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(AP_WORK)/modules/http \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = mod_asis + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) ASIS Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Mod_asis Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/mod_asis.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_asis.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @aprlib.imp \ + @httpd.imp \ + @libc.imp \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + asis_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/generators/NWGNUmod_cgi b/modules/generators/NWGNUmod_cgi new file mode 100644 index 0000000..bbb9578 --- /dev/null +++ b/modules/generators/NWGNUmod_cgi @@ -0,0 +1,249 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)/build/NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(AP_WORK)/modules/http \ + $(AP_WORK)/modules/filters \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = mod_cgi + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) CGI Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Mod_cgi Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/mod_cgi.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_cgi.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @aprlib.imp \ + @httpd.imp \ + @libc.imp \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + cgi_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + diff --git a/modules/generators/NWGNUstatus b/modules/generators/NWGNUstatus new file mode 100644 index 0000000..1dc850e --- /dev/null +++ b/modules/generators/NWGNUstatus @@ -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 = status + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Status Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Status Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/status.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_status.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @aprlib.imp \ + @httpd.imp \ + @libc.imp \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + status_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/generators/config5.m4 b/modules/generators/config5.m4 new file mode 100644 index 0000000..bf29521 --- /dev/null +++ b/modules/generators/config5.m4 @@ -0,0 +1,81 @@ +dnl modules enabled in this directory by default + +dnl APACHE_MODULE(name, helptext[, objects[, structname[, default[, config]]]]) + +APACHE_MODPATH_INIT(generators) + +APACHE_MODULE(status, process/thread monitoring, , , yes) +APACHE_MODULE(autoindex, directory listing, , , yes) +APACHE_MODULE(asis, as-is filetypes, , , ) +APACHE_MODULE(info, server information, , , most) +APACHE_MODULE(suexec, set uid and gid for spawned processes, , , no, [ + other_targets=suexec ] ) + +# Is mod_cgid needed? +case $host in + *mingw*) + dnl No fork+thread+fd issues, and cgid doesn't work anyway. + cgid_needed="no" + ;; + *) + if ap_mpm_is_threaded; then + dnl if we are using a threaded MPM on Unix, we can get better + dnl performance with mod_cgid, and also avoid potential issues + dnl with forking from a threaded process. + cgid_needed="yes" + else + dnl if we are using a non-threaded MPM, it makes little sense to + dnl use mod_cgid, and it just opens up holes we don't need. + cgid_needed="no" + fi + ;; +esac + +if test $cgid_needed = "yes"; then + APACHE_MODULE(cgid, CGI scripts. Enabled by default with threaded MPMs, , , most, [ + case $host in + *-solaris2*) + case `uname -r` in + 5.10) + dnl Does the system have the appropriate patches? + case `uname -p` in + i386) + patch_id="120665" + ;; + sparc) + patch_id="120664" + ;; + *) + AC_MSG_WARN([Unknown platform]) + patch_id="120664" + ;; + esac + AC_MSG_CHECKING([for Solaris patch $patch_id]) + showrev -p | grep "$patch_id" >/dev/null 2>&1 + if test $? -eq 1; then + dnl Solaris 11 (next release) as of snv_19 doesn't have this problem. + dnl It may be possible to use /kernel/drv/tl from later releases. + AC_MSG_ERROR([Please apply either patch # 120664 (Sparc) or # 120665 (x86). +Without these patches, mod_cgid is non-functional on Solaris 10 due to an OS +bug with AF_UNIX sockets. +If you can not apply these patches, you can do one of the following: + - run configure with --disable-cgid + - switch to the prefork MPM +For more info: ]) + else + AC_MSG_RESULT(yes) + fi + ;; + esac + ;; + esac + ]) + APACHE_MODULE(cgi, CGI scripts. Enabled by default with non-threaded MPMs, , , no) +else + APACHE_MODULE(cgi, CGI scripts. Enabled by default with non-threaded MPMs, , , most) + APACHE_MODULE(cgid, CGI scripts. Enabled by default with threaded MPMs, , , no) +fi + +APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/$modpath_current]) + +APACHE_MODPATH_FINISH diff --git a/modules/generators/mod_asis.c b/modules/generators/mod_asis.c new file mode 100644 index 0000000..c2b651b --- /dev/null +++ b/modules/generators/mod_asis.c @@ -0,0 +1,128 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "apr_strings.h" +#include "ap_config.h" +#include "httpd.h" +#include "http_config.h" +#include "http_protocol.h" +#include "http_log.h" +#include "util_script.h" +#include "http_main.h" +#include "http_request.h" + +#include "mod_core.h" + +#define ASIS_MAGIC_TYPE "httpd/send-as-is" + +static int asis_handler(request_rec *r) +{ + apr_file_t *f; + apr_status_t rv; + const char *location; + + if (strcmp(r->handler, ASIS_MAGIC_TYPE) && strcmp(r->handler, "send-as-is")) { + return DECLINED; + } + + r->allowed |= (AP_METHOD_BIT << M_GET); + if (r->method_number != M_GET) { + return DECLINED; + } + + if (r->finfo.filetype == APR_NOFILE) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01233) + "File does not exist: %s", r->filename); + return HTTP_NOT_FOUND; + } + + if ((rv = apr_file_open(&f, r->filename, APR_READ, + APR_OS_DEFAULT, r->pool)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01234) + "file permissions deny server access: %s", r->filename); + return HTTP_FORBIDDEN; + } + + ap_scan_script_header_err_ex(r, f, NULL, APLOG_MODULE_INDEX); + location = apr_table_get(r->headers_out, "Location"); + + if (location && location[0] == '/' && + ((r->status == HTTP_OK) || ap_is_HTTP_REDIRECT(r->status))) { + + apr_file_close(f); + + /* Internal redirect -- fake-up a pseudo-request */ + r->status = HTTP_OK; + + /* This redirect needs to be a GET no matter what the original + * method was. + */ + r->method = "GET"; + r->method_number = M_GET; + + ap_internal_redirect_handler(location, r); + return OK; + } + + if (!r->header_only) { + conn_rec *c = r->connection; + apr_bucket_brigade *bb; + apr_bucket *b; + apr_off_t pos = 0; + + rv = apr_file_seek(f, APR_CUR, &pos); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01235) + "mod_asis: failed to find end-of-headers position " + "for %s", r->filename); + apr_file_close(f); + return HTTP_INTERNAL_SERVER_ERROR; + } + + bb = apr_brigade_create(r->pool, c->bucket_alloc); + apr_brigade_insert_file(bb, f, pos, r->finfo.size - pos, r->pool); + + b = apr_bucket_eos_create(c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, b); + rv = ap_pass_brigade(r->output_filters, bb); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01236) + "mod_asis: ap_pass_brigade failed for file %s", r->filename); + return AP_FILTER_ERROR; + } + } + else { + apr_file_close(f); + } + + return OK; +} + +static void register_hooks(apr_pool_t *p) +{ + ap_hook_handler(asis_handler,NULL,NULL,APR_HOOK_MIDDLE); +} + +AP_DECLARE_MODULE(asis) = +{ + STANDARD20_MODULE_STUFF, + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + NULL, /* command apr_table_t */ + register_hooks /* register hooks */ +}; diff --git a/modules/generators/mod_asis.dep b/modules/generators/mod_asis.dep new file mode 100644 index 0000000..cbfe300 --- /dev/null +++ b/modules/generators/mod_asis.dep @@ -0,0 +1,56 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_asis.mak + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + + +.\mod_asis.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\httpd.h"\ + "..\..\include\mod_core.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_script.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + diff --git a/modules/generators/mod_asis.dsp b/modules/generators/mod_asis.dsp new file mode 100644 index 0000000..bad34f0 --- /dev/null +++ b/modules/generators/mod_asis.dsp @@ -0,0 +1,111 @@ +# Microsoft Developer Studio Project File - Name="mod_asis" - 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_asis - 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_asis.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_asis.mak" CFG="mod_asis - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_asis - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_asis - 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_asis - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_asis_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_asis.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_asis.so" /d LONG_NAME="asis_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:".\Release\mod_asis.so" /base:@..\..\os\win32\BaseAddr.ref,mod_asis.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_asis.so" /base:@..\..\os\win32\BaseAddr.ref,mod_asis.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_asis.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_asis - 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_asis_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_asis.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_asis.so" /d LONG_NAME="asis_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_asis.so" /base:@..\..\os\win32\BaseAddr.ref,mod_asis.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_asis.so" /base:@..\..\os\win32\BaseAddr.ref,mod_asis.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_asis.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_asis - Win32 Release" +# Name "mod_asis - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_asis.c +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/generators/mod_asis.exp b/modules/generators/mod_asis.exp new file mode 100644 index 0000000..4f347d9 --- /dev/null +++ b/modules/generators/mod_asis.exp @@ -0,0 +1 @@ +asis_module diff --git a/modules/generators/mod_asis.mak b/modules/generators/mod_asis.mak new file mode 100644 index 0000000..f069aac --- /dev/null +++ b/modules/generators/mod_asis.mak @@ -0,0 +1,353 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_asis.dsp +!IF "$(CFG)" == "" +CFG=mod_asis - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_asis - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_asis - Win32 Release" && "$(CFG)" != "mod_asis - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_asis.mak" CFG="mod_asis - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_asis - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_asis - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_asis - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_asis.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_asis.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_asis.obj" + -@erase "$(INTDIR)\mod_asis.res" + -@erase "$(INTDIR)\mod_asis_src.idb" + -@erase "$(INTDIR)\mod_asis_src.pdb" + -@erase "$(OUTDIR)\mod_asis.exp" + -@erase "$(OUTDIR)\mod_asis.lib" + -@erase "$(OUTDIR)\mod_asis.pdb" + -@erase "$(OUTDIR)\mod_asis.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_asis_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_asis.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_asis.so" /d LONG_NAME="asis_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_asis.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_asis.pdb" /debug /out:"$(OUTDIR)\mod_asis.so" /implib:"$(OUTDIR)\mod_asis.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_asis.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_asis.obj" \ + "$(INTDIR)\mod_asis.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" + +"$(OUTDIR)\mod_asis.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_asis.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_asis.so" + if exist .\Release\mod_asis.so.manifest mt.exe -manifest .\Release\mod_asis.so.manifest -outputresource:.\Release\mod_asis.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_asis - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_asis.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_asis.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_asis.obj" + -@erase "$(INTDIR)\mod_asis.res" + -@erase "$(INTDIR)\mod_asis_src.idb" + -@erase "$(INTDIR)\mod_asis_src.pdb" + -@erase "$(OUTDIR)\mod_asis.exp" + -@erase "$(OUTDIR)\mod_asis.lib" + -@erase "$(OUTDIR)\mod_asis.pdb" + -@erase "$(OUTDIR)\mod_asis.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_asis_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_asis.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_asis.so" /d LONG_NAME="asis_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_asis.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_asis.pdb" /debug /out:"$(OUTDIR)\mod_asis.so" /implib:"$(OUTDIR)\mod_asis.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_asis.so +LINK32_OBJS= \ + "$(INTDIR)\mod_asis.obj" \ + "$(INTDIR)\mod_asis.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" + +"$(OUTDIR)\mod_asis.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_asis.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_asis.so" + if exist .\Debug\mod_asis.so.manifest mt.exe -manifest .\Debug\mod_asis.so.manifest -outputresource:.\Debug\mod_asis.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_asis.dep") +!INCLUDE "mod_asis.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_asis.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_asis - Win32 Release" || "$(CFG)" == "mod_asis - Win32 Debug" + +!IF "$(CFG)" == "mod_asis - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\generators" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\generators" + +!ELSEIF "$(CFG)" == "mod_asis - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\generators" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\generators" + +!ENDIF + +!IF "$(CFG)" == "mod_asis - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\generators" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\generators" + +!ELSEIF "$(CFG)" == "mod_asis - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\generators" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\generators" + +!ENDIF + +!IF "$(CFG)" == "mod_asis - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\generators" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\generators" + +!ELSEIF "$(CFG)" == "mod_asis - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\generators" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\generators" + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_asis - Win32 Release" + + +"$(INTDIR)\mod_asis.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_asis.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_asis.so" /d LONG_NAME="asis_module for Apache" $(SOURCE) + + +!ELSEIF "$(CFG)" == "mod_asis - Win32 Debug" + + +"$(INTDIR)\mod_asis.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_asis.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_asis.so" /d LONG_NAME="asis_module for Apache" $(SOURCE) + + +!ENDIF + +SOURCE=.\mod_asis.c + +"$(INTDIR)\mod_asis.obj" : $(SOURCE) "$(INTDIR)" + + + +!ENDIF + diff --git a/modules/generators/mod_autoindex.c b/modules/generators/mod_autoindex.c new file mode 100644 index 0000000..9094e30 --- /dev/null +++ b/modules/generators/mod_autoindex.c @@ -0,0 +1,2348 @@ +/* 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_autoindex.c: Handles the on-the-fly html index generation + * + * Rob McCool + * 3/23/93 + * + * Adapted to Apache by rst. + * + * Version sort added by Martin Pool . + */ + +#include "apr_strings.h" +#include "apr_fnmatch.h" +#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_config.h" +#include "http_core.h" +#include "http_request.h" +#include "http_protocol.h" +#include "http_log.h" +#include "http_main.h" +#include "util_script.h" + +#include "mod_core.h" + +module AP_MODULE_DECLARE_DATA autoindex_module; + +/**************************************************************** + * + * Handling configuration directives... + */ + +#define NO_OPTIONS (1 << 0) /* Indexing options */ +#define ICONS_ARE_LINKS (1 << 1) +#define SCAN_HTML_TITLES (1 << 2) +#define SUPPRESS_ICON (1 << 3) +#define SUPPRESS_LAST_MOD (1 << 4) +#define SUPPRESS_SIZE (1 << 5) +#define SUPPRESS_DESC (1 << 6) +#define SUPPRESS_PREAMBLE (1 << 7) +#define SUPPRESS_COLSORT (1 << 8) +#define SUPPRESS_RULES (1 << 9) +#define FOLDERS_FIRST (1 << 10) +#define VERSION_SORT (1 << 11) +#define TRACK_MODIFIED (1 << 12) +#define FANCY_INDEXING (1 << 13) +#define TABLE_INDEXING (1 << 14) +#define IGNORE_CLIENT (1 << 15) +#define IGNORE_CASE (1 << 16) +#define EMIT_XHTML (1 << 17) +#define SHOW_FORBIDDEN (1 << 18) +#define ADDALTCLASS (1 << 19) +#define OPTION_UNSET (1 << 20) + +#define K_NOADJUST 0 +#define K_ADJUST 1 +#define K_UNSET 2 + +/* + * Define keys for sorting. + */ +#define K_NAME 'N' /* Sort by file name (default) */ +#define K_LAST_MOD 'M' /* Last modification date */ +#define K_SIZE 'S' /* Size (absolute, not as displayed) */ +#define K_DESC 'D' /* Description */ +#define K_VALID "NMSD" /* String containing _all_ valid K_ opts */ + +#define D_ASCENDING 'A' +#define D_DESCENDING 'D' +#define D_VALID "AD" /* String containing _all_ valid D_ opts */ + +/* + * These are the dimensions of the default icons supplied with Apache. + */ +#define DEFAULT_ICON_WIDTH 20 +#define DEFAULT_ICON_HEIGHT 22 + +/* + * Other default dimensions. + */ +#define DEFAULT_NAME_WIDTH 23 +#define DEFAULT_DESC_WIDTH 23 + +struct item { + char *type; + char *apply_to; + char *apply_path; + char *data; +}; + +typedef struct ai_desc_t { + char *pattern; + char *description; + int full_path; + int wildcards; +} ai_desc_t; + +typedef struct autoindex_config_struct { + + char *default_icon; + char *style_sheet; + char *head_insert; + char *header; + char *readme; + apr_int32_t opts; + apr_int32_t incremented_opts; + apr_int32_t decremented_opts; + int name_width; + int name_adjust; + int desc_width; + int desc_adjust; + int icon_width; + int icon_height; + char default_keyid; + char default_direction; + + apr_array_header_t *icon_list; + apr_array_header_t *alt_list; + apr_array_header_t *desc_list; + apr_array_header_t *ign_list; + int ign_noinherit; + + char *ctype; + char *charset; + char *datetime_format; +} autoindex_config_rec; + +static char c_by_encoding, c_by_type, c_by_path; + +#define BY_ENCODING &c_by_encoding +#define BY_TYPE &c_by_type +#define BY_PATH &c_by_path + +static APR_INLINE int response_is_html(request_rec *r) +{ + char *ctype = ap_field_noparam(r->pool, r->content_type); + + return !ap_cstr_casecmp(ctype, "text/html") + || !ap_cstr_casecmp(ctype, "application/xhtml+xml"); +} + +/* + * This routine puts the standard HTML header at the top of the index page. + * We include the DOCTYPE because we may be using features therefrom (i.e., + * HEIGHT and WIDTH attributes on the icons if we're FancyIndexing). + */ +static void emit_preamble(request_rec *r, int xhtml, const char *title) +{ + autoindex_config_rec *d; + + d = (autoindex_config_rec *) ap_get_module_config(r->per_dir_config, + &autoindex_module); + + if (xhtml) { + ap_rvputs(r, DOCTYPE_XHTML_1_0T, + "\n" + " \n Index of ", title, + "\n", NULL); + } else { + ap_rvputs(r, DOCTYPE_HTML_3_2, + "\n \n" + " Index of ", title, + "\n", NULL); + } + + if (d->style_sheet != NULL) { + ap_rvputs(r, " style_sheet, + "\" type=\"text/css\"", xhtml ? " />\n" : ">\n", NULL); + } + if (d->head_insert != NULL) { + ap_rputs(d->head_insert, r); + } + ap_rputs(" \n \n", r); +} + +static void push_item(apr_array_header_t *arr, char *type, const char *to, + const char *path, const char *data) +{ + struct item *p = (struct item *) apr_array_push(arr); + + if (!to) { + to = ""; + } + if (!path) { + path = ""; + } + + p->type = type; + p->data = apr_pstrdup(arr->pool, data); + p->apply_path = apr_pstrcat(arr->pool, path, "*", NULL); + + if ((type == BY_PATH) && (!ap_is_matchexp(to))) { + p->apply_to = apr_pstrcat(arr->pool, "*", to, NULL); + } + else { + p->apply_to = apr_pstrdup(arr->pool, to); + } +} + +static const char *add_alt(cmd_parms *cmd, void *d, const char *alt, + const char *to) +{ + if (cmd->info == BY_PATH) { + if (!strcmp(to, "**DIRECTORY**")) { + to = "^^DIRECTORY^^"; + } + } + if (cmd->info == BY_ENCODING) { + char *tmp = apr_pstrdup(cmd->temp_pool, to); + ap_str_tolower(tmp); + to = tmp; + } + + push_item(((autoindex_config_rec *) d)->alt_list, cmd->info, to, + cmd->path, alt); + return NULL; +} + +static const char *add_icon(cmd_parms *cmd, void *d, const char *icon, + const char *to) +{ + char *iconbak = apr_pstrdup(cmd->temp_pool, icon); + + if (icon[0] == '(') { + char *alt; + char *cl = strchr(iconbak, ')'); + + if (cl == NULL) { + return "missing closing paren"; + } + alt = ap_getword_nc(cmd->temp_pool, &iconbak, ','); + *cl = '\0'; /* Lose closing paren */ + add_alt(cmd, d, &alt[1], to); + } + if (cmd->info == BY_PATH) { + if (!strcmp(to, "**DIRECTORY**")) { + to = "^^DIRECTORY^^"; + } + } + if (cmd->info == BY_ENCODING) { + char *tmp = apr_pstrdup(cmd->temp_pool, to); + ap_str_tolower(tmp); + to = tmp; + } + + push_item(((autoindex_config_rec *) d)->icon_list, cmd->info, to, + cmd->path, iconbak); + return NULL; +} + +/* + * Add description text for a filename pattern. If the pattern has + * wildcards already (or we need to add them), add leading and + * trailing wildcards to it to ensure substring processing. If the + * pattern contains a '/' anywhere, force wildcard matching mode, + * add a slash to the prefix so that "bar/bletch" won't be matched + * by "foobar/bletch", and make a note that there's a delimiter; + * the matching routine simplifies to just the actual filename + * whenever it can. This allows definitions in parent directories + * to be made for files in subordinate ones using relative paths. + */ + +/* + * Absent a strcasestr() function, we have to force wildcards on + * systems for which "AAA" and "aaa" mean the same file. + */ +#ifdef CASE_BLIND_FILESYSTEM +#define WILDCARDS_REQUIRED 1 +#else +#define WILDCARDS_REQUIRED 0 +#endif + +static const char *add_desc(cmd_parms *cmd, void *d, const char *desc, + const char *to) +{ + autoindex_config_rec *dcfg = (autoindex_config_rec *) d; + ai_desc_t *desc_entry; + char *prefix = ""; + + desc_entry = (ai_desc_t *) apr_array_push(dcfg->desc_list); + desc_entry->full_path = (ap_strchr_c(to, '/') == NULL) ? 0 : 1; + desc_entry->wildcards = (WILDCARDS_REQUIRED + || desc_entry->full_path + || apr_fnmatch_test(to)); + if (desc_entry->wildcards) { + prefix = desc_entry->full_path ? "*/" : "*"; + desc_entry->pattern = apr_pstrcat(dcfg->desc_list->pool, + prefix, to, "*", NULL); + } + else { + desc_entry->pattern = apr_pstrdup(dcfg->desc_list->pool, to); + } + desc_entry->description = apr_pstrdup(dcfg->desc_list->pool, desc); + return NULL; +} + +static const char *add_ignore(cmd_parms *cmd, void *d, const char *ext) +{ + push_item(((autoindex_config_rec *) d)->ign_list, 0, ext, cmd->path, NULL); + return NULL; +} + +static const char *add_opts(cmd_parms *cmd, void *d, int argc, char *const argv[]) +{ + int i; + char *w; + apr_int32_t opts; + apr_int32_t opts_add; + apr_int32_t opts_remove; + char action; + autoindex_config_rec *d_cfg = (autoindex_config_rec *) d; + + opts = d_cfg->opts; + opts_add = d_cfg->incremented_opts; + opts_remove = d_cfg->decremented_opts; + + for (i = 0; i < argc; i++) { + int option = 0; + w = argv[i]; + + if ((*w == '+') || (*w == '-')) { + action = *(w++); + } + else { + action = '\0'; + } + if (!strcasecmp(w, "FancyIndexing")) { + option = FANCY_INDEXING; + } + else if (!strcasecmp(w, "FoldersFirst")) { + option = FOLDERS_FIRST; + } + else if (!strcasecmp(w, "HTMLTable")) { + option = TABLE_INDEXING; + } + else if (!strcasecmp(w, "IconsAreLinks")) { + option = ICONS_ARE_LINKS; + } + else if (!strcasecmp(w, "IgnoreCase")) { + option = IGNORE_CASE; + } + else if (!strcasecmp(w, "IgnoreClient")) { + option = IGNORE_CLIENT; + } + else if (!strcasecmp(w, "ScanHTMLTitles")) { + option = SCAN_HTML_TITLES; + } + else if (!strcasecmp(w, "SuppressColumnSorting")) { + option = SUPPRESS_COLSORT; + } + else if (!strcasecmp(w, "SuppressDescription")) { + option = SUPPRESS_DESC; + } + else if (!strcasecmp(w, "SuppressHTMLPreamble")) { + option = SUPPRESS_PREAMBLE; + } + else if (!strcasecmp(w, "SuppressIcon")) { + option = SUPPRESS_ICON; + } + else if (!strcasecmp(w, "SuppressLastModified")) { + option = SUPPRESS_LAST_MOD; + } + else if (!strcasecmp(w, "SuppressSize")) { + option = SUPPRESS_SIZE; + } + else if (!strcasecmp(w, "SuppressRules")) { + option = SUPPRESS_RULES; + } + else if (!strcasecmp(w, "TrackModified")) { + option = TRACK_MODIFIED; + } + else if (!strcasecmp(w, "VersionSort")) { + option = VERSION_SORT; + } + else if (!strcasecmp(w, "XHTML")) { + option = EMIT_XHTML; + } + else if (!strcasecmp(w, "ShowForbidden")) { + option = SHOW_FORBIDDEN; + } + else if (!strcasecmp(w, "AddAltClass")) { + option = ADDALTCLASS; + } + else if (!strcasecmp(w, "None")) { + if (action != '\0') { + return "Cannot combine '+' or '-' with 'None' keyword"; + } + opts = NO_OPTIONS; + opts_add = 0; + opts_remove = 0; + } + else if (!strcasecmp(w, "IconWidth")) { + if (action != '-') { + d_cfg->icon_width = DEFAULT_ICON_WIDTH; + } + else { + d_cfg->icon_width = 0; + } + } + else if (!strncasecmp(w, "IconWidth=", 10)) { + if (action == '-') { + return "Cannot combine '-' with IconWidth=n"; + } + d_cfg->icon_width = atoi(&w[10]); + } + else if (!strcasecmp(w, "IconHeight")) { + if (action != '-') { + d_cfg->icon_height = DEFAULT_ICON_HEIGHT; + } + else { + d_cfg->icon_height = 0; + } + } + else if (!strncasecmp(w, "IconHeight=", 11)) { + if (action == '-') { + return "Cannot combine '-' with IconHeight=n"; + } + d_cfg->icon_height = atoi(&w[11]); + } + else if (!strcasecmp(w, "NameWidth")) { + if (action != '-') { + return "NameWidth with no value may only appear as " + "'-NameWidth'"; + } + d_cfg->name_width = DEFAULT_NAME_WIDTH; + d_cfg->name_adjust = K_NOADJUST; + } + else if (!strncasecmp(w, "NameWidth=", 10)) { + if (action == '-') { + return "Cannot combine '-' with NameWidth=n"; + } + if (w[10] == '*') { + d_cfg->name_adjust = K_ADJUST; + } + else { + int width = atoi(&w[10]); + + if (width && (width < 5)) { + return "NameWidth value must be greater than 5"; + } + d_cfg->name_width = width; + d_cfg->name_adjust = K_NOADJUST; + } + } + else if (!strcasecmp(w, "DescriptionWidth")) { + if (action != '-') { + return "DescriptionWidth with no value may only appear as " + "'-DescriptionWidth'"; + } + d_cfg->desc_width = DEFAULT_DESC_WIDTH; + d_cfg->desc_adjust = K_NOADJUST; + } + else if (!strncasecmp(w, "DescriptionWidth=", 17)) { + if (action == '-') { + return "Cannot combine '-' with DescriptionWidth=n"; + } + if (w[17] == '*') { + d_cfg->desc_adjust = K_ADJUST; + } + else { + int width = atoi(&w[17]); + + if (width && (width < 12)) { + return "DescriptionWidth value must be greater than 12"; + } + d_cfg->desc_width = width; + d_cfg->desc_adjust = K_NOADJUST; + } + } + else if (!strncasecmp(w, "Type=", 5)) { + d_cfg->ctype = apr_pstrdup(cmd->pool, &w[5]); + } + else if (!strncasecmp(w, "Charset=", 8)) { + d_cfg->charset = apr_pstrdup(cmd->pool, &w[8]); + } + else if (!strcasecmp(w, "UseOldDateFormat")) { + d_cfg->datetime_format = "%d-%b-%Y %H:%M"; + } + else { + return "Invalid directory indexing option"; + } + if (action == '\0') { + opts |= option; + opts_add = 0; + opts_remove = 0; + } + else if (action == '+') { + opts_add |= option; + opts_remove &= ~option; + } + else { + opts_remove |= option; + opts_add &= ~option; + } + } + if ((opts & NO_OPTIONS) && (opts & ~NO_OPTIONS)) { + return "Cannot combine other IndexOptions keywords with 'None'"; + } + d_cfg->incremented_opts = opts_add; + d_cfg->decremented_opts = opts_remove; + d_cfg->opts = opts; + return NULL; +} + +static const char *set_default_order(cmd_parms *cmd, void *m, + const char *direction, const char *key) +{ + autoindex_config_rec *d_cfg = (autoindex_config_rec *) m; + + if (!strcasecmp(direction, "Ascending")) { + d_cfg->default_direction = D_ASCENDING; + } + else if (!strcasecmp(direction, "Descending")) { + d_cfg->default_direction = D_DESCENDING; + } + else { + return "First keyword must be 'Ascending' or 'Descending'"; + } + + if (!strcasecmp(key, "Name")) { + d_cfg->default_keyid = K_NAME; + } + else if (!strcasecmp(key, "Date")) { + d_cfg->default_keyid = K_LAST_MOD; + } + else if (!strcasecmp(key, "Size")) { + d_cfg->default_keyid = K_SIZE; + } + else if (!strcasecmp(key, "Description")) { + d_cfg->default_keyid = K_DESC; + } + else { + return "Second keyword must be 'Name', 'Date', 'Size', or " + "'Description'"; + } + + return NULL; +} + +#define DIR_CMD_PERMS OR_INDEXES + +static const command_rec autoindex_cmds[] = +{ + AP_INIT_ITERATE2("AddIcon", add_icon, BY_PATH, DIR_CMD_PERMS, + "an icon URL followed by one or more filenames"), + AP_INIT_ITERATE2("AddIconByType", add_icon, BY_TYPE, DIR_CMD_PERMS, + "an icon URL followed by one or more MIME types"), + AP_INIT_ITERATE2("AddIconByEncoding", add_icon, BY_ENCODING, DIR_CMD_PERMS, + "an icon URL followed by one or more content encodings"), + AP_INIT_ITERATE2("AddAlt", add_alt, BY_PATH, DIR_CMD_PERMS, + "alternate descriptive text followed by one or more " + "filenames"), + AP_INIT_ITERATE2("AddAltByType", add_alt, BY_TYPE, DIR_CMD_PERMS, + "alternate descriptive text followed by one or more MIME " + "types"), + AP_INIT_ITERATE2("AddAltByEncoding", add_alt, BY_ENCODING, DIR_CMD_PERMS, + "alternate descriptive text followed by one or more " + "content encodings"), + AP_INIT_TAKE_ARGV("IndexOptions", add_opts, NULL, DIR_CMD_PERMS, + "one or more index options [+|-][]"), + AP_INIT_TAKE2("IndexOrderDefault", set_default_order, NULL, DIR_CMD_PERMS, + "{Ascending,Descending} {Name,Size,Description,Date}"), + AP_INIT_ITERATE("IndexIgnore", add_ignore, NULL, DIR_CMD_PERMS, + "one or more file extensions"), + AP_INIT_FLAG("IndexIgnoreReset", ap_set_flag_slot, + (void *)APR_OFFSETOF(autoindex_config_rec, ign_noinherit), + DIR_CMD_PERMS, + "Reset the inherited list of IndexIgnore filenames"), + AP_INIT_ITERATE2("AddDescription", add_desc, BY_PATH, DIR_CMD_PERMS, + "Descriptive text followed by one or more filenames"), + AP_INIT_TAKE1("HeaderName", ap_set_string_slot, + (void *)APR_OFFSETOF(autoindex_config_rec, header), + DIR_CMD_PERMS, "a filename"), + AP_INIT_TAKE1("ReadmeName", ap_set_string_slot, + (void *)APR_OFFSETOF(autoindex_config_rec, readme), + DIR_CMD_PERMS, "a filename"), + AP_INIT_RAW_ARGS("FancyIndexing", ap_set_deprecated, NULL, OR_ALL, + "The FancyIndexing directive is no longer supported. " + "Use IndexOptions FancyIndexing."), + AP_INIT_TAKE1("DefaultIcon", ap_set_string_slot, + (void *)APR_OFFSETOF(autoindex_config_rec, default_icon), + DIR_CMD_PERMS, "an icon URL"), + AP_INIT_TAKE1("IndexStyleSheet", ap_set_string_slot, + (void *)APR_OFFSETOF(autoindex_config_rec, style_sheet), + DIR_CMD_PERMS, "URL to style sheet"), + AP_INIT_TAKE1("IndexHeadInsert", ap_set_string_slot, + (void *)APR_OFFSETOF(autoindex_config_rec, head_insert), + DIR_CMD_PERMS, "String to insert in HTML HEAD section"), + {NULL} +}; + +static void *create_autoindex_config(apr_pool_t *p, char *dummy) +{ + autoindex_config_rec *new = + (autoindex_config_rec *) apr_pcalloc(p, sizeof(autoindex_config_rec)); + + new->icon_width = 0; + new->icon_height = 0; + new->name_width = DEFAULT_NAME_WIDTH; + new->name_adjust = K_UNSET; + new->desc_width = DEFAULT_DESC_WIDTH; + new->desc_adjust = K_UNSET; + new->icon_list = apr_array_make(p, 4, sizeof(struct item)); + new->alt_list = apr_array_make(p, 4, sizeof(struct item)); + new->desc_list = apr_array_make(p, 4, sizeof(ai_desc_t)); + new->ign_list = apr_array_make(p, 4, sizeof(struct item)); + new->opts = OPTION_UNSET; + new->incremented_opts = 0; + new->decremented_opts = 0; + new->default_keyid = '\0'; + new->default_direction = '\0'; + + return (void *) new; +} + +static void *merge_autoindex_configs(apr_pool_t *p, void *basev, void *addv) +{ + autoindex_config_rec *new; + autoindex_config_rec *base = (autoindex_config_rec *) basev; + autoindex_config_rec *add = (autoindex_config_rec *) addv; + + new = (autoindex_config_rec *) apr_pcalloc(p, sizeof(autoindex_config_rec)); + new->default_icon = add->default_icon ? add->default_icon + : base->default_icon; + new->style_sheet = add->style_sheet ? add->style_sheet + : base->style_sheet; + new->head_insert = add->head_insert ? add->head_insert + : base->head_insert; + new->header = add->header ? add->header + : base->header; + new->readme = add->readme ? add->readme + : base->readme; + new->icon_height = add->icon_height ? add->icon_height : base->icon_height; + new->icon_width = add->icon_width ? add->icon_width : base->icon_width; + + new->ctype = add->ctype ? add->ctype : base->ctype; + new->charset = add->charset ? add->charset : base->charset; + new->datetime_format = add->datetime_format ? add->datetime_format : base->datetime_format; + + new->alt_list = apr_array_append(p, add->alt_list, base->alt_list); + new->desc_list = apr_array_append(p, add->desc_list, base->desc_list); + new->icon_list = apr_array_append(p, add->icon_list, base->icon_list); + new->ign_list = add->ign_noinherit ? add->ign_list : apr_array_append(p, add->ign_list, base->ign_list); + if (add->opts == NO_OPTIONS) { + /* + * If the current directory explicitly says 'no options' then we also + * clear any incremental mods from being inheritable further down. + */ + new->opts = NO_OPTIONS; + new->incremented_opts = 0; + new->decremented_opts = 0; + } + else { + /* + * If there were any nonincremental options selected for + * this directory, they dominate and we don't inherit *anything.* + * Contrariwise, we *do* inherit if the only settings here are + * incremental ones. + */ + if (add->opts == OPTION_UNSET) { + new->incremented_opts = (base->incremented_opts + | add->incremented_opts) + & ~add->decremented_opts; + new->decremented_opts = (base->decremented_opts + | add->decremented_opts); + /* + * We may have incremental settings, so make sure we don't + * inadvertently inherit an IndexOptions None from above. + */ + new->opts = (base->opts & ~NO_OPTIONS); + } + else { + /* + * There are local nonincremental settings, which clear + * all inheritance from above. They *are* the new base settings. + */ + new->opts = add->opts; + } + /* + * We're guaranteed that there'll be no overlap between + * the add-options and the remove-options. + */ + new->opts |= new->incremented_opts; + new->opts &= ~new->decremented_opts; + } + /* + * Inherit the NameWidth settings if there aren't any specific to + * the new location; otherwise we'll end up using the defaults set in the + * config-rec creation routine. + */ + if (add->name_adjust == K_UNSET) { + new->name_width = base->name_width; + new->name_adjust = base->name_adjust; + } + else { + new->name_width = add->name_width; + new->name_adjust = add->name_adjust; + } + + /* + * Likewise for DescriptionWidth. + */ + if (add->desc_adjust == K_UNSET) { + new->desc_width = base->desc_width; + new->desc_adjust = base->desc_adjust; + } + else { + new->desc_width = add->desc_width; + new->desc_adjust = add->desc_adjust; + } + + new->default_keyid = add->default_keyid ? add->default_keyid + : base->default_keyid; + new->default_direction = add->default_direction ? add->default_direction + : base->default_direction; + return new; +} + +/**************************************************************** + * + * Looking things up in config entries... + */ + +/* Structure used to hold entries when we're actually building an index */ + +struct ent { + char *name; + char *icon; + char *alt; + char *desc; + apr_off_t size; + apr_time_t lm; + struct ent *next; + int ascending, ignore_case, version_sort; + char key; + int isdir; +}; + +static char *find_item(const char *content_type, const char *content_encoding, + char *path, apr_array_header_t *list, int path_only) +{ + struct item *items = (struct item *) list->elts; + int i; + + for (i = 0; i < list->nelts; ++i) { + struct item *p = &items[i]; + + /* Special cased for ^^DIRECTORY^^ and ^^BLANKICON^^ */ + if ((path[0] == '^') || (!ap_strcmp_match(path, p->apply_path))) { + if (!*(p->apply_to)) { + return p->data; + } + else if (p->type == BY_PATH || path[0] == '^') { + if (!ap_strcmp_match(path, p->apply_to)) { + return p->data; + } + } + else if (!path_only) { + if (!content_encoding) { + if (p->type == BY_TYPE) { + if (content_type + && !ap_strcasecmp_match(content_type, + p->apply_to)) { + return p->data; + } + } + } + else { + if (p->type == BY_ENCODING) { + if (!ap_strcasecmp_match(content_encoding, + p->apply_to)) { + return p->data; + } + } + } + } + } + } + return NULL; +} + +static char *find_item_by_request(request_rec *r, apr_array_header_t *list, int path_only) +{ + return find_item(ap_field_noparam(r->pool, r->content_type), + r->content_encoding, r->filename, list, path_only); +} + +#define find_icon(d,p,t) find_item_by_request(p,d->icon_list,t) +#define find_alt(d,p,t) find_item_by_request(p,d->alt_list,t) +#define find_default_icon(d,n) find_item(NULL, NULL, n, d->icon_list, 1) +#define find_default_alt(d,n) find_item(NULL, NULL, n, d->alt_list, 1) + +/* + * Look through the list of pattern/description pairs and return the first one + * if any) that matches the filename in the request. If multiple patterns + * match, only the first one is used; since the order in the array is the + * same as the order in which directives were processed, earlier matching + * directives will dominate. + */ + +#ifdef CASE_BLIND_FILESYSTEM +#define MATCH_FLAGS APR_FNM_CASE_BLIND +#else +#define MATCH_FLAGS 0 +#endif + +static char *find_desc(autoindex_config_rec *dcfg, const char *filename_full) +{ + int i; + ai_desc_t *list = (ai_desc_t *) dcfg->desc_list->elts; + const char *filename_only; + const char *filename; + + /* + * If the filename includes a path, extract just the name itself + * for the simple matches. + */ + if ((filename_only = ap_strrchr_c(filename_full, '/')) == NULL) { + filename_only = filename_full; + } + else { + filename_only++; + } + for (i = 0; i < dcfg->desc_list->nelts; ++i) { + ai_desc_t *tuple = &list[i]; + int found; + + /* + * Only use the full-path filename if the pattern contains '/'s. + */ + filename = (tuple->full_path) ? filename_full : filename_only; + /* + * Make the comparison using the cheapest method; only do + * wildcard checking if we must. + */ + if (tuple->wildcards) { + found = (apr_fnmatch(tuple->pattern, filename, MATCH_FLAGS) == 0); + } + else { + found = (ap_strstr_c(filename, tuple->pattern) != NULL); + } + if (found) { + return tuple->description; + } + } + return NULL; +} + +static int ignore_entry(autoindex_config_rec *d, char *path) +{ + apr_array_header_t *list = d->ign_list; + struct item *items = (struct item *) list->elts; + char *tt; + int i; + + if ((tt = strrchr(path, '/')) == NULL) { + tt = path; + } + else { + tt++; + } + + for (i = 0; i < list->nelts; ++i) { + struct item *p = &items[i]; + char *ap; + + if ((ap = strrchr(p->apply_to, '/')) == NULL) { + ap = p->apply_to; + } + else { + ap++; + } + +#ifndef CASE_BLIND_FILESYSTEM + if (!ap_strcmp_match(path, p->apply_path) + && !ap_strcmp_match(tt, ap)) { + return 1; + } +#else /* !CASE_BLIND_FILESYSTEM */ + /* + * On some platforms, the match must be case-blind. This is really + * a factor of the filesystem involved, but we can't detect that + * reliably - so we have to granularise at the OS level. + */ + if (!ap_strcasecmp_match(path, p->apply_path) + && !ap_strcasecmp_match(tt, ap)) { + return 1; + } +#endif /* !CASE_BLIND_FILESYSTEM */ + } + return 0; +} + +/***************************************************************** + * + * Actually generating output + */ + +/* + * Elements of the emitted document: + * Preamble + * Emitted unless SUPPRESS_PREAMBLE is set AND ap_run_sub_req + * succeeds for the (content_type == text/html) header file. + * Header file + * Emitted if found (and able). + * H1 tag line + * Emitted if a header file is NOT emitted. + * Directory stuff + * Always emitted. + * HR + * Emitted if FANCY_INDEXING is set. + * Readme file + * Emitted if found (and able). + * ServerSig + * Emitted if ServerSignature is not Off AND a readme file + * is NOT emitted. + * Postamble + * Emitted unless SUPPRESS_PREAMBLE is set AND ap_run_sub_req + * succeeds for the (content_type == text/html) readme file. + */ + + +/* + * emit a plain text file + */ +static void do_emit_plain(request_rec *r, apr_file_t *f) +{ + char buf[AP_IOBUFSIZE + 1]; + int ch; + apr_size_t i, c, n; + apr_status_t rv; + + ap_rputs("
\n", r);
+    while (!apr_file_eof(f)) {
+        do {
+            n = sizeof(char) * AP_IOBUFSIZE;
+            rv = apr_file_read(f, buf, &n);
+        } while (APR_STATUS_IS_EINTR(rv));
+        if (n == 0 || rv != APR_SUCCESS) {
+            /* ###: better error here? */
+            break;
+        }
+        buf[n] = '\0';
+        c = 0;
+        while (c < n) {
+            for (i = c; i < n; i++) {
+                if (buf[i] == '<' || buf[i] == '>' || buf[i] == '&') {
+                    break;
+                }
+            }
+            ch = buf[i];
+            buf[i] = '\0';
+            ap_rputs(&buf[c], r);
+            if (ch == '<') {
+                ap_rputs("<", r);
+            }
+            else if (ch == '>') {
+                ap_rputs(">", r);
+            }
+            else if (ch == '&') {
+                ap_rputs("&", r);
+            }
+            c = i + 1;
+        }
+    }
+    ap_rputs("
\n", r); +} + +/* + * Handle the preamble through the H1 tag line, inclusive. Locate + * the file with a subrequests. Process text/html documents by actually + * running the subrequest; text/xxx documents get copied verbatim, + * and any other content type is ignored. This means that a non-text + * document (such as HEADER.gif) might get multiviewed as the result + * instead of a text document, meaning nothing will be displayed, but + * oh well. + */ +static void emit_head(request_rec *r, char *header_fname, int suppress_amble, + int emit_xhtml, char *title) +{ + autoindex_config_rec *d; + apr_table_t *hdrs = r->headers_in; + apr_file_t *f = NULL; + request_rec *rr = NULL; + int emit_amble = 1; + int emit_H1 = 1; + const char *r_accept; + const char *r_accept_enc; + + /* + * If there's a header file, send a subrequest to look for it. If it's + * found and html do the subrequest, otherwise handle it + */ + r_accept = apr_table_get(hdrs, "Accept"); + r_accept_enc = apr_table_get(hdrs, "Accept-Encoding"); + apr_table_setn(hdrs, "Accept", "text/html, text/plain"); + apr_table_unset(hdrs, "Accept-Encoding"); + + + if ((header_fname != NULL) && r->args) { + header_fname = apr_pstrcat(r->pool, header_fname, "?", r->args, NULL); + } + + if ((header_fname != NULL) + && (rr = ap_sub_req_lookup_uri(header_fname, r, r->output_filters)) + && (rr->status == HTTP_OK) + && (rr->filename != NULL) + && (rr->finfo.filetype == APR_REG)) { + /* + * Check for the two specific cases we allow: text/html and + * text/anything-else. The former is allowed to be processed for + * SSIs. + */ + if (rr->content_type != NULL) { + if (response_is_html(rr)) { + ap_filter_t *f; + /* Hope everything will work... */ + emit_amble = 0; + emit_H1 = 0; + + if (! suppress_amble) { + emit_preamble(r, emit_xhtml, title); + } + /* This is a hack, but I can't find any better way to do this. + * The problem is that we have already created the sub-request, + * but we just inserted the OLD_WRITE filter, and the + * sub-request needs to pass its data through the OLD_WRITE + * filter, or things go horribly wrong (missing data, data in + * the wrong order, etc). To fix it, if you create a + * sub-request and then insert the OLD_WRITE filter before you + * run the request, you need to make sure that the sub-request + * data goes through the OLD_WRITE filter. Just steal this + * code. The long-term solution is to remove the ap_r* + * functions. + */ + for (f=rr->output_filters; + f->frec != ap_subreq_core_filter_handle; f = f->next); + f->next = r->output_filters; + + /* + * If there's a problem running the subrequest, display the + * preamble if we didn't do it before -- the header file + * didn't get displayed. + */ + if (ap_run_sub_req(rr) != OK) { + /* It didn't work */ + emit_amble = suppress_amble; + emit_H1 = 1; + } + } + else if (!strncasecmp("text/", rr->content_type, 5)) { + /* + * If we can open the file, prefix it with the preamble + * regardless; since we'll be sending a
 block around
+                 * the file's contents, any HTML header it had won't end up
+                 * where it belongs.
+                 */
+                if (apr_file_open(&f, rr->filename, APR_READ,
+                                  APR_OS_DEFAULT, r->pool) == APR_SUCCESS) {
+                    emit_preamble(r, emit_xhtml, title);
+                    emit_amble = 0;
+                    do_emit_plain(r, f);
+                    apr_file_close(f);
+                    emit_H1 = 0;
+                }
+            }
+        }
+    }
+
+    if (r_accept) {
+        apr_table_setn(hdrs, "Accept", r_accept);
+    }
+    else {
+        apr_table_unset(hdrs, "Accept");
+    }
+
+    if (r_accept_enc) {
+        apr_table_setn(hdrs, "Accept-Encoding", r_accept_enc);
+    }
+
+    if (emit_amble) {
+        emit_preamble(r, emit_xhtml, title);
+    }
+
+    d = (autoindex_config_rec *) ap_get_module_config(r->per_dir_config, &autoindex_module);
+
+    if (emit_H1) {
+        if (d->style_sheet != NULL) {
+            /* Insert style id if stylesheet used */
+            ap_rvputs(r, "  

Index of ", title, "

\n", NULL); + } else { + ap_rvputs(r, "

Index of ", title, "

\n", NULL); + } + } + if (rr != NULL) { + ap_destroy_sub_req(rr); + } +} + + +/* + * Handle the Readme file through the postamble, inclusive. Locate + * the file with a subrequests. Process text/html documents by actually + * running the subrequest; text/xxx documents get copied verbatim, + * and any other content type is ignored. This means that a non-text + * document (such as FOOTER.gif) might get multiviewed as the result + * instead of a text document, meaning nothing will be displayed, but + * oh well. + */ +static void emit_tail(request_rec *r, char *readme_fname, int suppress_amble) +{ + apr_file_t *f = NULL; + request_rec *rr = NULL; + int suppress_post = 0; + int suppress_sig = 0; + + /* + * If there's a readme file, send a subrequest to look for it. If it's + * found and a text file, handle it -- otherwise fall through and + * pretend there's nothing there. + */ + if ((readme_fname != NULL) + && (rr = ap_sub_req_lookup_uri(readme_fname, r, r->output_filters)) + && (rr->status == HTTP_OK) + && (rr->filename != NULL) + && rr->finfo.filetype == APR_REG) { + /* + * Check for the two specific cases we allow: text/html and + * text/anything-else. The former is allowed to be processed for + * SSIs. + */ + if (rr->content_type != NULL) { + if (response_is_html(rr)) { + ap_filter_t *f; + for (f=rr->output_filters; + f->frec != ap_subreq_core_filter_handle; f = f->next); + f->next = r->output_filters; + + + if (ap_run_sub_req(rr) == OK) { + /* worked... */ + suppress_sig = 1; + suppress_post = suppress_amble; + } + } + else if (!strncasecmp("text/", rr->content_type, 5)) { + /* + * If we can open the file, suppress the signature. + */ + if (apr_file_open(&f, rr->filename, APR_READ, + APR_OS_DEFAULT, r->pool) == APR_SUCCESS) { + do_emit_plain(r, f); + apr_file_close(f); + suppress_sig = 1; + } + } + } + } + + if (!suppress_sig) { + ap_rputs(ap_psignature("", r), r); + } + if (!suppress_post) { + ap_rputs("\n", r); + } + if (rr != NULL) { + ap_destroy_sub_req(rr); + } +} + + +static char *find_title(request_rec *r) +{ + char titlebuf[MAX_STRING_LEN], *find = ""; + apr_file_t *thefile = NULL; + int x, y, p; + apr_size_t n; + + if (r->status != HTTP_OK) { + return NULL; + } + if ((r->content_type != NULL) + && (response_is_html(r) + || !strcmp(r->content_type, INCLUDES_MAGIC_TYPE)) + && !r->content_encoding) { + if (apr_file_open(&thefile, r->filename, APR_READ, + APR_OS_DEFAULT, r->pool) != APR_SUCCESS) { + return NULL; + } + n = sizeof(char) * (MAX_STRING_LEN - 1); + apr_file_read(thefile, titlebuf, &n); + if (n == 0) { + apr_file_close(thefile); + return NULL; + } + titlebuf[n] = '\0'; + for (x = 0, p = 0; titlebuf[x]; x++) { + if (apr_tolower(titlebuf[x]) == find[p]) { + if (!find[++p]) { + if ((p = ap_ind(&titlebuf[++x], '<')) != -1) { + titlebuf[x + p] = '\0'; + } + /* Scan for line breaks for Tanmoy's secretary */ + for (y = x; titlebuf[y]; y++) { + if ((titlebuf[y] == CR) || (titlebuf[y] == LF)) { + if (y == x) { + x++; + } + else { + titlebuf[y] = ' '; + } + } + } + apr_file_close(thefile); + return apr_pstrdup(r->pool, &titlebuf[x]); + } + } + else { + p = 0; + } + } + apr_file_close(thefile); + } + return NULL; +} + +static struct ent *make_parent_entry(apr_int32_t autoindex_opts, + autoindex_config_rec *d, + request_rec *r, char keyid, + char direction) +{ + struct ent *p = (struct ent *) apr_pcalloc(r->pool, sizeof(struct ent)); + char *testpath; + /* + * p->name is now the true parent URI. + * testpath is a crafted lie, so that the syntax '/some/..' + * (or simply '..')be used to describe 'up' from '/some/' + * when processeing IndexIgnore, and Icon|Alt|Desc configs. + */ + + /* The output has always been to the parent. Don't make ourself + * our own parent (worthless cyclical reference). + */ + if (!(p->name = ap_make_full_path(r->pool, r->uri, "../"))) { + return (NULL); + } + ap_getparents(p->name); + if (!*p->name) { + return (NULL); + } + + /* IndexIgnore has always compared "/thispath/.." */ + testpath = ap_make_full_path(r->pool, r->filename, ".."); + if (ignore_entry(d, testpath)) { + return (NULL); + } + + p->size = -1; + p->lm = -1; + p->key = apr_toupper(keyid); + p->ascending = (apr_toupper(direction) == D_ASCENDING); + p->version_sort = autoindex_opts & VERSION_SORT; + if (autoindex_opts & FANCY_INDEXING) { + if (!(p->icon = find_default_icon(d, testpath))) { + p->icon = find_default_icon(d, "^^DIRECTORY^^"); + } + if (!(p->alt = find_default_alt(d, testpath))) { + if (!(p->alt = find_default_alt(d, "^^DIRECTORY^^"))) { + /* Special alt text for parent dir to distinguish it from other directories + this is essential when trying to style this dir entry via AddAltClass */ + p->alt = "PARENTDIR"; + } + } + p->desc = find_desc(d, testpath); + } + return p; +} + +static struct ent *make_autoindex_entry(const apr_finfo_t *dirent, + int autoindex_opts, + autoindex_config_rec *d, + request_rec *r, char keyid, + char direction, + const char *pattern) +{ + request_rec *rr; + struct ent *p; + int show_forbidden = 0; + + /* Dot is ignored, Parent is handled by make_parent_entry() */ + if ((dirent->name[0] == '.') && (!dirent->name[1] + || ((dirent->name[1] == '.') && !dirent->name[2]))) + return (NULL); + + /* + * On some platforms, the match must be case-blind. This is really + * a factor of the filesystem involved, but we can't detect that + * reliably - so we have to granularise at the OS level. + */ + if (pattern && (apr_fnmatch(pattern, dirent->name, + APR_FNM_NOESCAPE | APR_FNM_PERIOD +#ifdef CASE_BLIND_FILESYSTEM + | APR_FNM_CASE_BLIND +#endif + ) + != APR_SUCCESS)) { + return (NULL); + } + + if (ignore_entry(d, ap_make_full_path(r->pool, + r->filename, dirent->name))) { + return (NULL); + } + + if (!(rr = ap_sub_req_lookup_dirent(dirent, r, AP_SUBREQ_NO_ARGS, NULL))) { + return (NULL); + } + + if ((autoindex_opts & SHOW_FORBIDDEN) + && (rr->status == HTTP_UNAUTHORIZED || rr->status == HTTP_FORBIDDEN)) { + show_forbidden = 1; + } + + if ((rr->finfo.filetype != APR_DIR && rr->finfo.filetype != APR_REG) + || !(rr->status == OK || ap_is_HTTP_SUCCESS(rr->status) + || ap_is_HTTP_REDIRECT(rr->status) + || show_forbidden == 1)) { + ap_destroy_sub_req(rr); + return (NULL); + } + + p = (struct ent *) apr_pcalloc(r->pool, sizeof(struct ent)); + if (dirent->filetype == APR_DIR) { + p->name = apr_pstrcat(r->pool, dirent->name, "/", NULL); + } + else { + p->name = apr_pstrdup(r->pool, dirent->name); + } + p->size = -1; + p->icon = NULL; + p->alt = NULL; + p->desc = NULL; + p->lm = -1; + p->isdir = 0; + p->key = apr_toupper(keyid); + p->ascending = (apr_toupper(direction) == D_ASCENDING); + p->version_sort = !!(autoindex_opts & VERSION_SORT); + p->ignore_case = !!(autoindex_opts & IGNORE_CASE); + + if (autoindex_opts & (FANCY_INDEXING | TABLE_INDEXING)) { + p->lm = rr->finfo.mtime; + if (dirent->filetype == APR_DIR) { + if (autoindex_opts & FOLDERS_FIRST) { + p->isdir = 1; + } + rr->filename = ap_make_dirstr_parent (rr->pool, rr->filename); + + /* omit the trailing slash (1.3 compat) */ + rr->filename[strlen(rr->filename) - 1] = '\0'; + + if (!(p->icon = find_icon(d, rr, 1))) { + p->icon = find_default_icon(d, "^^DIRECTORY^^"); + } + if (!(p->alt = find_alt(d, rr, 1))) { + if (!(p->alt = find_default_alt(d, "^^DIRECTORY^^"))) { + p->alt = "DIR"; + } + } + } + else { + p->icon = find_icon(d, rr, 0); + p->alt = find_alt(d, rr, 0); + p->size = rr->finfo.size; + } + + p->desc = find_desc(d, rr->filename); + + if ((!p->desc) && (autoindex_opts & SCAN_HTML_TITLES)) { + p->desc = apr_pstrdup(r->pool, find_title(rr)); + } + } + ap_destroy_sub_req(rr); + /* + * We don't need to take any special action for the file size key. + * If we did, it would go here. + */ + if (keyid == K_LAST_MOD) { + if (p->lm < 0) { + p->lm = 0; + } + } + return (p); +} + +static char *terminate_description(autoindex_config_rec *d, char *desc, + apr_int32_t autoindex_opts, int desc_width) +{ + int maxsize = desc_width; + int x; + + /* + * If there's no DescriptionWidth in effect, default to the old + * behaviour of adjusting the description size depending upon + * what else is being displayed. Otherwise, stick with the + * setting. + */ + if (d->desc_adjust == K_UNSET) { + if (autoindex_opts & SUPPRESS_ICON) { + maxsize += 6; + } + if (autoindex_opts & SUPPRESS_LAST_MOD) { + maxsize += 19; + } + if (autoindex_opts & SUPPRESS_SIZE) { + maxsize += 7; + } + } + for (x = 0; desc[x] && ((maxsize > 0) || (desc[x] == '<')); x++) { + if (desc[x] == '<') { + while (desc[x] != '>') { + if (!desc[x]) { + maxsize = 0; + break; + } + ++x; + } + } + else if (desc[x] == '&') { + /* entities like ä count as one character */ + --maxsize; + for ( ; desc[x] != ';'; ++x) { + if (desc[x] == '\0') { + maxsize = 0; + break; + } + } + } + else { + --maxsize; + } + } + if (!maxsize && desc[x] != '\0') { + desc[x - 1] = '>'; /* Grump. */ + desc[x] = '\0'; /* Double Grump! */ + } + return desc; +} + +/* + * Emit the anchor for the specified field. If a field is the key for the + * current request, the link changes its meaning to reverse the order when + * selected again. Non-active fields always start in ascending order. + */ +static void emit_link(request_rec *r, const char *anchor, char column, + char curkey, char curdirection, + const char *colargs, int nosort) +{ + if (!nosort) { + char qvalue[9]; + + qvalue[0] = '?'; + qvalue[1] = 'C'; + qvalue[2] = '='; + qvalue[3] = column; + qvalue[4] = ';'; + qvalue[5] = 'O'; + qvalue[6] = '='; + /* reverse? */ + qvalue[7] = ((curkey == column) && (curdirection == D_ASCENDING)) + ? D_DESCENDING : D_ASCENDING; + qvalue[8] = '\0'; + ap_rvputs(r, "<a href=\"", qvalue, colargs ? colargs : "", + "\">", anchor, "</a>", NULL); + } + else { + ap_rputs(anchor, r); + } +} + +static void output_directories(struct ent **ar, int n, + autoindex_config_rec *d, request_rec *r, + apr_int32_t autoindex_opts, char keyid, + char direction, const char *colargs) +{ + int x; + apr_size_t rv; + char *tp; + int static_columns = !!(autoindex_opts & SUPPRESS_COLSORT); + apr_pool_t *scratch; + int name_width; + int desc_width; + char *datetime_format; + char *name_scratch; + char *pad_scratch; + char *breakrow = ""; + + apr_pool_create(&scratch, r->pool); + + name_width = d->name_width; + desc_width = d->desc_width; + datetime_format = d->datetime_format ? d->datetime_format : "%Y-%m-%d %H:%M"; + + if ((autoindex_opts & (FANCY_INDEXING | TABLE_INDEXING)) + == FANCY_INDEXING) { + if (d->name_adjust == K_ADJUST) { + for (x = 0; x < n; x++) { + int t = strlen(ar[x]->name); + if (t > name_width) { + name_width = t; + } + } + } + + if (d->desc_adjust == K_ADJUST) { + for (x = 0; x < n; x++) { + if (ar[x]->desc != NULL) { + int t = strlen(ar[x]->desc); + if (t > desc_width) { + desc_width = t; + } + } + } + } + } + name_scratch = apr_palloc(r->pool, name_width + 1); + pad_scratch = apr_palloc(r->pool, name_width + 1); + memset(pad_scratch, ' ', name_width); + pad_scratch[name_width] = '\0'; + + if (autoindex_opts & TABLE_INDEXING) { + int cols = 1; + if (d->style_sheet != NULL) { + /* Emit table with style id */ + ap_rputs(" <table id=\"indexlist\">\n <tr class=\"indexhead\">", r); + } else { + ap_rputs(" <table>\n <tr>", r); + } + if (!(autoindex_opts & SUPPRESS_ICON)) { + ap_rvputs(r, "<th", (d->style_sheet != NULL) ? " class=\"indexcolicon\">" : " valign=\"top\">", NULL); + if ((tp = find_default_icon(d, "^^BLANKICON^^"))) { + ap_rvputs(r, "<img src=\"", ap_escape_html(scratch, tp), + "\" alt=\"[ICO]\"", NULL); + if (d->icon_width) { + ap_rprintf(r, " width=\"%d\"", d->icon_width); + } + if (d->icon_height) { + ap_rprintf(r, " height=\"%d\"", d->icon_height); + } + + if (autoindex_opts & EMIT_XHTML) { + ap_rputs(" /", r); + } + ap_rputs("></th>", r); + } + else { + ap_rputs(" </th>", r); + } + + ++cols; + } + ap_rvputs(r, "<th", (d->style_sheet != NULL) ? " class=\"indexcolname\">" : ">", NULL); + emit_link(r, "Name", K_NAME, keyid, direction, + colargs, static_columns); + if (!(autoindex_opts & SUPPRESS_LAST_MOD)) { + ap_rvputs(r, "</th><th", (d->style_sheet != NULL) ? " class=\"indexcollastmod\">" : ">", NULL); + emit_link(r, "Last modified", K_LAST_MOD, keyid, direction, + colargs, static_columns); + ++cols; + } + if (!(autoindex_opts & SUPPRESS_SIZE)) { + ap_rvputs(r, "</th><th", (d->style_sheet != NULL) ? " class=\"indexcolsize\">" : ">", NULL); + emit_link(r, "Size", K_SIZE, keyid, direction, + colargs, static_columns); + ++cols; + } + if (!(autoindex_opts & SUPPRESS_DESC)) { + ap_rvputs(r, "</th><th", (d->style_sheet != NULL) ? " class=\"indexcoldesc\">" : ">", NULL); + emit_link(r, "Description", K_DESC, keyid, direction, + colargs, static_columns); + ++cols; + } + if (!(autoindex_opts & SUPPRESS_RULES)) { + breakrow = apr_psprintf(r->pool, + " <tr%s><th colspan=\"%d\">" + "<hr%s></th></tr>\n", + (d->style_sheet != NULL) ? " class=\"indexbreakrow\"" : "", + cols, + (autoindex_opts & EMIT_XHTML) ? " /" : ""); + } + ap_rvputs(r, "</th></tr>\n", breakrow, NULL); + } + else if (autoindex_opts & FANCY_INDEXING) { + ap_rputs("<pre>", r); + if (!(autoindex_opts & SUPPRESS_ICON)) { + if ((tp = find_default_icon(d, "^^BLANKICON^^"))) { + ap_rvputs(r, "<img src=\"", ap_escape_html(scratch, tp), + "\" alt=\"Icon \"", NULL); + if (d->icon_width) { + ap_rprintf(r, " width=\"%d\"", d->icon_width); + } + if (d->icon_height) { + ap_rprintf(r, " height=\"%d\"", d->icon_height); + } + + if (autoindex_opts & EMIT_XHTML) { + ap_rputs(" /", r); + } + ap_rputs("> ", r); + } + else { + ap_rputs(" ", r); + } + } + emit_link(r, "Name", K_NAME, keyid, direction, + colargs, static_columns); + ap_rputs(pad_scratch + 4, r); + /* + * Emit the guaranteed-at-least-one-space-between-columns byte. + */ + ap_rputs(" ", r); + if (!(autoindex_opts & SUPPRESS_LAST_MOD)) { + emit_link(r, "Last modified", K_LAST_MOD, keyid, direction, + colargs, static_columns); + ap_rputs(" ", r); + } + if (!(autoindex_opts & SUPPRESS_SIZE)) { + emit_link(r, "Size", K_SIZE, keyid, direction, + colargs, static_columns); + ap_rputs(" ", r); + } + if (!(autoindex_opts & SUPPRESS_DESC)) { + emit_link(r, "Description", K_DESC, keyid, direction, + colargs, static_columns); + } + if (!(autoindex_opts & SUPPRESS_RULES)) { + ap_rputs("<hr", r); + if (autoindex_opts & EMIT_XHTML) { + ap_rputs(" /", r); + } + ap_rputs(">", r); + } + else { + ap_rputc('\n', r); + } + } + else { + ap_rputs("<ul>", r); + } + + for (x = 0; x < n; x++) { + char *anchor, *t, *t2; + int nwidth; + + apr_pool_clear(scratch); + + t = ar[x]->name; + anchor = ap_escape_html(scratch, ap_os_escape_path(scratch, t, 0)); + + if (!x && t[0] == '/') { + t2 = "Parent Directory"; + } + else { + t2 = t; + } + + if (autoindex_opts & TABLE_INDEXING) { + /* Even/Odd rows for IndexStyleSheet */ + if (d->style_sheet != NULL) { + if (ar[x]->alt && (autoindex_opts & ADDALTCLASS)) { + /* Include alt text in class name, distinguish between odd and even rows */ + char *altclass = apr_pstrdup(scratch, ar[x]->alt); + ap_str_tolower(altclass); + ap_rvputs(r, " <tr class=\"", ( x & 0x1) ? "odd-" : "even-", altclass, "\">", NULL); + } else { + /* Distinguish between odd and even rows */ + ap_rvputs(r, " <tr class=\"", ( x & 0x1) ? "odd" : "even", "\">", NULL); + } + } else { + ap_rputs("<tr>", r); + } + + if (!(autoindex_opts & SUPPRESS_ICON)) { + ap_rvputs(r, "<td", (d->style_sheet != NULL) ? " class=\"indexcolicon\">" : " valign=\"top\">", NULL); + if (autoindex_opts & ICONS_ARE_LINKS) { + ap_rvputs(r, "<a href=\"", anchor, "\">", NULL); + } + if ((ar[x]->icon) || d->default_icon) { + ap_rvputs(r, "<img src=\"", + ap_escape_html(scratch, + ar[x]->icon ? ar[x]->icon + : d->default_icon), + "\" alt=\"[", (ar[x]->alt ? ar[x]->alt : " "), + "]\"", NULL); + if (d->icon_width) { + ap_rprintf(r, " width=\"%d\"", d->icon_width); + } + if (d->icon_height) { + ap_rprintf(r, " height=\"%d\"", d->icon_height); + } + + if (autoindex_opts & EMIT_XHTML) { + ap_rputs(" /", r); + } + ap_rputs(">", r); + } + else { + ap_rputs(" ", r); + } + if (autoindex_opts & ICONS_ARE_LINKS) { + ap_rputs("</a></td>", r); + } + else { + ap_rputs("</td>", r); + } + } + if (d->name_adjust == K_ADJUST) { + ap_rvputs(r, "<td", (d->style_sheet != NULL) ? " class=\"indexcolname\">" : ">", "<a href=\"", anchor, "\">", + ap_escape_html(scratch, t2), "</a>", NULL); + } + else { + nwidth = strlen(t2); + if (nwidth > name_width) { + memcpy(name_scratch, t2, name_width - 3); + name_scratch[name_width - 3] = '.'; + name_scratch[name_width - 2] = '.'; + name_scratch[name_width - 1] = '>'; + name_scratch[name_width] = 0; + t2 = name_scratch; + nwidth = name_width; + } + ap_rvputs(r, "<td", (d->style_sheet != NULL) ? " class=\"indexcolname\">" : ">", "<a href=\"", anchor, "\">", + ap_escape_html(scratch, t2), + "</a>", pad_scratch + nwidth, NULL); + } + if (!(autoindex_opts & SUPPRESS_LAST_MOD)) { + if (ar[x]->lm != -1) { + char time_str[32]; + apr_time_exp_t ts; + apr_time_exp_lt(&ts, ar[x]->lm); + apr_strftime(time_str, &rv, sizeof(time_str), + datetime_format, + &ts); + ap_rvputs(r, "</td><td", (d->style_sheet != NULL) ? " class=\"indexcollastmod\">" : " align=\"right\">", time_str, " ", NULL); + } + else { + ap_rvputs(r, "</td><td", (d->style_sheet != NULL) ? " class=\"indexcollastmod\"> " : "> ", NULL); + } + } + if (!(autoindex_opts & SUPPRESS_SIZE)) { + char buf[5]; + ap_rvputs(r, "</td><td", (d->style_sheet != NULL) ? " class=\"indexcolsize\">" : " align=\"right\">", + apr_strfsize(ar[x]->size, buf), NULL); + } + if (!(autoindex_opts & SUPPRESS_DESC)) { + if (ar[x]->desc) { + if (d->desc_adjust == K_ADJUST) { + ap_rvputs(r, "</td><td", (d->style_sheet != NULL) ? " class=\"indexcoldesc\">" : ">", ar[x]->desc, NULL); + } + else { + ap_rvputs(r, "</td><td", (d->style_sheet != NULL) ? " class=\"indexcoldesc\">" : ">", + terminate_description(d, ar[x]->desc, + autoindex_opts, + desc_width), NULL); + } + } + else { + ap_rvputs(r, "</td><td", (d->style_sheet != NULL) ? " class=\"indexcoldesc\">" : ">", " ", NULL); + } + } + ap_rputs("</td></tr>\n", r); + } + else if (autoindex_opts & FANCY_INDEXING) { + if (!(autoindex_opts & SUPPRESS_ICON)) { + if (autoindex_opts & ICONS_ARE_LINKS) { + ap_rvputs(r, "<a href=\"", anchor, "\">", NULL); + } + if ((ar[x]->icon) || d->default_icon) { + ap_rvputs(r, "<img src=\"", + ap_escape_html(scratch, + ar[x]->icon ? ar[x]->icon + : d->default_icon), + "\" alt=\"[", (ar[x]->alt ? ar[x]->alt : " "), + "]\"", NULL); + if (d->icon_width) { + ap_rprintf(r, " width=\"%d\"", d->icon_width); + } + if (d->icon_height) { + ap_rprintf(r, " height=\"%d\"", d->icon_height); + } + + if (autoindex_opts & EMIT_XHTML) { + ap_rputs(" /", r); + } + ap_rputs(">", r); + } + else { + ap_rputs(" ", r); + } + if (autoindex_opts & ICONS_ARE_LINKS) { + ap_rputs("</a> ", r); + } + else { + ap_rputc(' ', r); + } + } + nwidth = strlen(t2); + if (nwidth > name_width) { + memcpy(name_scratch, t2, name_width - 3); + name_scratch[name_width - 3] = '.'; + name_scratch[name_width - 2] = '.'; + name_scratch[name_width - 1] = '>'; + name_scratch[name_width] = 0; + t2 = name_scratch; + nwidth = name_width; + } + ap_rvputs(r, "<a href=\"", anchor, "\">", + ap_escape_html(scratch, t2), + "</a>", pad_scratch + nwidth, NULL); + /* + * The blank before the storm.. er, before the next field. + */ + ap_rputs(" ", r); + if (!(autoindex_opts & SUPPRESS_LAST_MOD)) { + if (ar[x]->lm != -1) { + char time_str[32]; + apr_time_exp_t ts; + apr_time_exp_lt(&ts, ar[x]->lm); + apr_strftime(time_str, &rv, sizeof(time_str), + datetime_format, + &ts); + ap_rvputs(r, time_str, " ", NULL); + } + else { + /* Length="1975-04-07 01:23 " (default in 2.4 and later) or + * Length="07-Apr-1975 01:24 ". (2.2 and UseOldDateFormat) + * See 'datetime_format' above. + */ + ap_rputs(" ", r); + } + } + if (!(autoindex_opts & SUPPRESS_SIZE)) { + char buf[5]; + ap_rputs(apr_strfsize(ar[x]->size, buf), r); + ap_rputs(" ", r); + } + if (!(autoindex_opts & SUPPRESS_DESC)) { + if (ar[x]->desc) { + ap_rputs(terminate_description(d, ar[x]->desc, + autoindex_opts, + desc_width), r); + } + } + ap_rputc('\n', r); + } + else { + ap_rvputs(r, "<li><a href=\"", anchor, "\"> ", + ap_escape_html(scratch, t2), + "</a></li>\n", NULL); + } + } + if (autoindex_opts & TABLE_INDEXING) { + ap_rvputs(r, breakrow, "</table>\n", NULL); + } + else if (autoindex_opts & FANCY_INDEXING) { + if (!(autoindex_opts & SUPPRESS_RULES)) { + ap_rputs("<hr", r); + if (autoindex_opts & EMIT_XHTML) { + ap_rputs(" /", r); + } + ap_rputs("></pre>\n", r); + } + else { + ap_rputs("</pre>\n", r); + } + } + else { + ap_rputs("</ul>\n", r); + } +} + +/* + * Compare two file entries according to the sort criteria. The return + * is essentially a signum function value. + */ + +static int dsortf(struct ent **e1, struct ent **e2) +{ + struct ent *c1; + struct ent *c2; + int result = 0; + + /* + * First, see if either of the entries is for the parent directory. + * If so, that *always* sorts lower than anything else. + */ + if ((*e1)->name[0] == '/') { + return -1; + } + if ((*e2)->name[0] == '/') { + return 1; + } + /* + * Now see if one's a directory and one isn't, if we're set + * isdir for FOLDERS_FIRST. + */ + if ((*e1)->isdir != (*e2)->isdir) { + return (*e1)->isdir ? -1 : 1; + } + /* + * All of our comparisons will be of the c1 entry against the c2 one, + * so assign them appropriately to take care of the ordering. + */ + if ((*e1)->ascending) { + c1 = *e1; + c2 = *e2; + } + else { + c1 = *e2; + c2 = *e1; + } + + switch (c1->key) { + case K_LAST_MOD: + if (c1->lm > c2->lm) { + return 1; + } + else if (c1->lm < c2->lm) { + return -1; + } + break; + case K_SIZE: + if (c1->size > c2->size) { + return 1; + } + else if (c1->size < c2->size) { + return -1; + } + break; + case K_DESC: + if (c1->version_sort) { + result = apr_strnatcmp(c1->desc ? c1->desc : "", + c2->desc ? c2->desc : ""); + } + else { + result = strcmp(c1->desc ? c1->desc : "", + c2->desc ? c2->desc : ""); + } + if (result) { + return result; + } + break; + } + + /* names may identical when treated case-insensitively, + * so always fall back on strcmp() flavors to put entries + * in deterministic order. This means that 'ABC' and 'abc' + * will always appear in the same order, rather than + * variably between 'ABC abc' and 'abc ABC' order. + */ + + if (c1->version_sort) { + if (c1->ignore_case) { + result = apr_strnatcasecmp (c1->name, c2->name); + } + if (!result) { + result = apr_strnatcmp(c1->name, c2->name); + } + } + + /* The names may be identical in respects other than + * filename case when strnatcmp is used above, so fall back + * to strcmp on conflicts so that fn1.01.zzz and fn1.1.zzz + * are also sorted in a deterministic order. + */ + + if (!result && c1->ignore_case) { + result = strcasecmp (c1->name, c2->name); + } + + if (!result) { + result = strcmp (c1->name, c2->name); + } + + return result; +} + + +static int index_directory(request_rec *r, + autoindex_config_rec *autoindex_conf) +{ + char *title_name = ap_escape_html(r->pool, r->uri); + char *title_endp; + char *name = r->filename; + char *pstring = NULL; + apr_finfo_t dirent; + apr_dir_t *thedir; + apr_status_t status; + int num_ent = 0, x; + struct ent *head, *p; + struct ent **ar = NULL; + const char *qstring; + apr_int32_t autoindex_opts = autoindex_conf->opts; + char keyid; + char direction; + char *colargs; + char *fullpath; + apr_size_t dirpathlen; + char *ctype = "text/html"; + char *charset; + + if ((status = apr_dir_open(&thedir, name, r->pool)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01275) + "Can't open directory for index: %s", r->filename); + return HTTP_FORBIDDEN; + } + + if (autoindex_conf->ctype) { + ctype = autoindex_conf->ctype; + } + if (autoindex_conf->charset) { + charset = autoindex_conf->charset; + } + else { +#if APR_HAS_UNICODE_FS + charset = "UTF-8"; +#else + charset = "ISO-8859-1"; +#endif + } + if (*charset) { + ap_set_content_type(r, apr_pstrcat(r->pool, ctype, ";charset=", + charset, NULL)); + } + else { + ap_set_content_type(r, ctype); + } + + if (autoindex_opts & TRACK_MODIFIED) { + ap_update_mtime(r, r->finfo.mtime); + ap_set_last_modified(r); + ap_set_etag(r); + } + if (r->header_only) { + apr_dir_close(thedir); + return 0; + } + + /* + * If there is no specific ordering defined for this directory, + * default to ascending by filename. + */ + keyid = autoindex_conf->default_keyid + ? autoindex_conf->default_keyid : K_NAME; + direction = autoindex_conf->default_direction + ? autoindex_conf->default_direction : D_ASCENDING; + + /* + * Figure out what sort of indexing (if any) we're supposed to use. + * + * If no QUERY_STRING was specified or client query strings have been + * explicitly disabled. + * If we are ignoring the client, suppress column sorting as well. + */ + if (autoindex_opts & IGNORE_CLIENT) { + qstring = NULL; + autoindex_opts |= SUPPRESS_COLSORT; + colargs = ""; + } + else { + char fval[5], vval[5], *ppre = "", *epattern = ""; + fval[0] = '\0'; vval[0] = '\0'; + qstring = r->args; + + while (qstring && *qstring) { + + /* C= First Sort key Column (N, M, S, D) */ + if ( qstring[0] == 'C' && qstring[1] == '=' + && qstring[2] && strchr(K_VALID, qstring[2]) + && ( qstring[3] == '&' || qstring[3] == ';' + || !qstring[3])) { + keyid = qstring[2]; + qstring += qstring[3] ? 4 : 3; + } + + /* O= Sort order (A, D) */ + else if ( qstring[0] == 'O' && qstring[1] == '=' + && ( (qstring[2] == D_ASCENDING) + || (qstring[2] == D_DESCENDING)) + && ( qstring[3] == '&' || qstring[3] == ';' + || !qstring[3])) { + direction = qstring[2]; + qstring += qstring[3] ? 4 : 3; + } + + /* F= Output Format (0 plain, 1 fancy (pre), 2 table) */ + else if ( qstring[0] == 'F' && qstring[1] == '=' + && qstring[2] && strchr("012", qstring[2]) + && ( qstring[3] == '&' || qstring[3] == ';' + || !qstring[3])) { + if (qstring[2] == '0') { + autoindex_opts &= ~(FANCY_INDEXING | TABLE_INDEXING); + } + else if (qstring[2] == '1') { + autoindex_opts = (autoindex_opts | FANCY_INDEXING) + & ~TABLE_INDEXING; + } + else if (qstring[2] == '2') { + autoindex_opts |= FANCY_INDEXING | TABLE_INDEXING; + } + strcpy(fval, ";F= "); + fval[3] = qstring[2]; + qstring += qstring[3] ? 4 : 3; + } + + /* V= Version sort (0, 1) */ + else if ( qstring[0] == 'V' && qstring[1] == '=' + && (qstring[2] == '0' || qstring[2] == '1') + && ( qstring[3] == '&' || qstring[3] == ';' + || !qstring[3])) { + if (qstring[2] == '0') { + autoindex_opts &= ~VERSION_SORT; + } + else if (qstring[2] == '1') { + autoindex_opts |= VERSION_SORT; + } + strcpy(vval, ";V= "); + vval[3] = qstring[2]; + qstring += qstring[3] ? 4 : 3; + } + + /* P= wildcard pattern (*.foo) */ + else if (qstring[0] == 'P' && qstring[1] == '=') { + const char *eos = qstring += 2; /* for efficiency */ + + while (*eos && *eos != '&' && *eos != ';') { + ++eos; + } + + if (eos == qstring) { + pstring = NULL; + } + else { + pstring = apr_pstrndup(r->pool, qstring, eos - qstring); + if (ap_unescape_url(pstring) != OK) { + /* ignore the pattern, if it's bad. */ + pstring = NULL; + } + else { + ppre = ";P="; + /* be correct */ + epattern = ap_escape_uri(r->pool, pstring); + } + } + + if (*eos && *++eos) { + qstring = eos; + } + else { + qstring = NULL; + } + } + + /* Syntax error? Ignore the remainder! */ + else { + qstring = NULL; + } + } + colargs = apr_pstrcat(r->pool, fval, vval, ppre, epattern, NULL); + } + + /* Spew HTML preamble */ + title_endp = title_name + strlen(title_name) - 1; + + while (title_endp > title_name && *title_endp == '/') { + *title_endp-- = '\0'; + } + + emit_head(r, autoindex_conf->header, + autoindex_opts & SUPPRESS_PREAMBLE, + autoindex_opts & EMIT_XHTML, title_name); + + /* + * Since we don't know how many dir. entries there are, put them into a + * linked list and then arrayificate them so qsort can use them. + */ + head = NULL; + p = make_parent_entry(autoindex_opts, autoindex_conf, r, keyid, direction); + if (p != NULL) { + p->next = head; + head = p; + num_ent++; + } + fullpath = apr_palloc(r->pool, APR_PATH_MAX); + dirpathlen = strlen(name); + memcpy(fullpath, name, dirpathlen); + + do { + status = apr_dir_read(&dirent, APR_FINFO_MIN | APR_FINFO_NAME, thedir); + if (APR_STATUS_IS_INCOMPLETE(status)) { + continue; /* ignore un-stat()able files */ + } + else if (status != APR_SUCCESS) { + break; + } + + /* We want to explode symlinks here. */ + if (dirent.filetype == APR_LNK) { + const char *savename; + apr_finfo_t fi; + /* We *must* have FNAME. */ + savename = dirent.name; + apr_cpystrn(fullpath + dirpathlen, dirent.name, + APR_PATH_MAX - dirpathlen); + status = apr_stat(&fi, fullpath, + dirent.valid & ~(APR_FINFO_NAME), r->pool); + if (status != APR_SUCCESS) { + /* Something bad happened, skip this file. */ + continue; + } + memcpy(&dirent, &fi, sizeof(fi)); + dirent.name = savename; + dirent.valid |= APR_FINFO_NAME; + } + p = make_autoindex_entry(&dirent, autoindex_opts, autoindex_conf, r, + keyid, direction, pstring); + if (p != NULL) { + p->next = head; + head = p; + num_ent++; + } + } while (1); + + if (num_ent > 0) { + ar = (struct ent **) apr_palloc(r->pool, + num_ent * sizeof(struct ent *)); + p = head; + x = 0; + while (p) { + ar[x++] = p; + p = p->next; + } + + qsort((void *) ar, num_ent, sizeof(struct ent *), + (int (*)(const void *, const void *)) dsortf); + } + output_directories(ar, num_ent, autoindex_conf, r, autoindex_opts, + keyid, direction, colargs); + apr_dir_close(thedir); + + emit_tail(r, autoindex_conf->readme, + autoindex_opts & SUPPRESS_PREAMBLE); + + return 0; +} + +/* The formal handler... */ + +static int handle_autoindex(request_rec *r) +{ + autoindex_config_rec *d; + int allow_opts; + + if (strcmp(r->handler,DIR_MAGIC_TYPE) && !AP_IS_DEFAULT_HANDLER_NAME(r->handler)) { + return DECLINED; + } + if (r->finfo.filetype != APR_DIR) { + return DECLINED; + } + + allow_opts = ap_allow_options(r); + + d = (autoindex_config_rec *) ap_get_module_config(r->per_dir_config, + &autoindex_module); + + r->allowed |= (AP_METHOD_BIT << M_GET); + if (r->method_number != M_GET) { + return DECLINED; + } + + /* OK, nothing easy. Trot out the heavy artillery... */ + + if (allow_opts & OPT_INDEXES) { + int errstatus; + + if ((errstatus = ap_discard_request_body(r)) != OK) { + return errstatus; + } + + /* KLUDGE --- make the sub_req lookups happen in the right directory. + * Fixing this in the sub_req_lookup functions themselves is difficult, + * and would probably break virtual includes... + */ + + if (r->filename[strlen(r->filename) - 1] != '/') { + r->filename = apr_pstrcat(r->pool, r->filename, "/", NULL); + } + return index_directory(r, d); + } + else { + const char *index_names = apr_table_get(r->notes, "dir-index-names"); + + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01276) + "Cannot serve directory %s: No matching DirectoryIndex (%s) found, and " + "server-generated directory index forbidden by " + "Options directive", + r->filename, + index_names ? index_names : "none"); + return HTTP_FORBIDDEN; + } +} + +static void register_hooks(apr_pool_t *p) +{ + ap_hook_handler(handle_autoindex,NULL,NULL,APR_HOOK_MIDDLE); +} + +AP_DECLARE_MODULE(autoindex) = +{ + STANDARD20_MODULE_STUFF, + create_autoindex_config, /* dir config creater */ + merge_autoindex_configs, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + autoindex_cmds, /* command apr_table_t */ + register_hooks /* register hooks */ +}; diff --git a/modules/generators/mod_autoindex.dep b/modules/generators/mod_autoindex.dep new file mode 100644 index 0000000..1d5ca61 --- /dev/null +++ b/modules/generators/mod_autoindex.dep @@ -0,0 +1,61 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_autoindex.mak + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + + +.\mod_autoindex.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\httpd.h"\ + "..\..\include\mod_core.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_script.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_fnmatch.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + diff --git a/modules/generators/mod_autoindex.dsp b/modules/generators/mod_autoindex.dsp new file mode 100644 index 0000000..1f6ec05 --- /dev/null +++ b/modules/generators/mod_autoindex.dsp @@ -0,0 +1,111 @@ +# Microsoft Developer Studio Project File - Name="mod_autoindex" - 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_autoindex - 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_autoindex.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_autoindex.mak" CFG="mod_autoindex - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_autoindex - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_autoindex - 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_autoindex - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_autoindex_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_autoindex.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_autoindex.so" /d LONG_NAME="autoindex_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:".\Release\mod_autoindex.so" /base:@..\..\os\win32\BaseAddr.ref,mod_autoindex.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_autoindex.so" /base:@..\..\os\win32\BaseAddr.ref,mod_autoindex.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_autoindex.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_autoindex - 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_autoindex_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_autoindex.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_autoindex.so" /d LONG_NAME="autoindex_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_autoindex.so" /base:@..\..\os\win32\BaseAddr.ref,mod_autoindex.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_autoindex.so" /base:@..\..\os\win32\BaseAddr.ref,mod_autoindex.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_autoindex.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_autoindex - Win32 Release" +# Name "mod_autoindex - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_autoindex.c +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/generators/mod_autoindex.exp b/modules/generators/mod_autoindex.exp new file mode 100644 index 0000000..90f4057 --- /dev/null +++ b/modules/generators/mod_autoindex.exp @@ -0,0 +1 @@ +autoindex_module diff --git a/modules/generators/mod_autoindex.mak b/modules/generators/mod_autoindex.mak new file mode 100644 index 0000000..daebb5d --- /dev/null +++ b/modules/generators/mod_autoindex.mak @@ -0,0 +1,353 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_autoindex.dsp +!IF "$(CFG)" == "" +CFG=mod_autoindex - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_autoindex - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_autoindex - Win32 Release" && "$(CFG)" != "mod_autoindex - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_autoindex.mak" CFG="mod_autoindex - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_autoindex - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_autoindex - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_autoindex - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_autoindex.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_autoindex.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_autoindex.obj" + -@erase "$(INTDIR)\mod_autoindex.res" + -@erase "$(INTDIR)\mod_autoindex_src.idb" + -@erase "$(INTDIR)\mod_autoindex_src.pdb" + -@erase "$(OUTDIR)\mod_autoindex.exp" + -@erase "$(OUTDIR)\mod_autoindex.lib" + -@erase "$(OUTDIR)\mod_autoindex.pdb" + -@erase "$(OUTDIR)\mod_autoindex.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_autoindex_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_autoindex.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_autoindex.so" /d LONG_NAME="autoindex_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_autoindex.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_autoindex.pdb" /debug /out:"$(OUTDIR)\mod_autoindex.so" /implib:"$(OUTDIR)\mod_autoindex.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_autoindex.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_autoindex.obj" \ + "$(INTDIR)\mod_autoindex.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" + +"$(OUTDIR)\mod_autoindex.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_autoindex.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_autoindex.so" + if exist .\Release\mod_autoindex.so.manifest mt.exe -manifest .\Release\mod_autoindex.so.manifest -outputresource:.\Release\mod_autoindex.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_autoindex - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_autoindex.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_autoindex.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_autoindex.obj" + -@erase "$(INTDIR)\mod_autoindex.res" + -@erase "$(INTDIR)\mod_autoindex_src.idb" + -@erase "$(INTDIR)\mod_autoindex_src.pdb" + -@erase "$(OUTDIR)\mod_autoindex.exp" + -@erase "$(OUTDIR)\mod_autoindex.lib" + -@erase "$(OUTDIR)\mod_autoindex.pdb" + -@erase "$(OUTDIR)\mod_autoindex.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_autoindex_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_autoindex.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_autoindex.so" /d LONG_NAME="autoindex_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_autoindex.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_autoindex.pdb" /debug /out:"$(OUTDIR)\mod_autoindex.so" /implib:"$(OUTDIR)\mod_autoindex.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_autoindex.so +LINK32_OBJS= \ + "$(INTDIR)\mod_autoindex.obj" \ + "$(INTDIR)\mod_autoindex.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" + +"$(OUTDIR)\mod_autoindex.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_autoindex.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_autoindex.so" + if exist .\Debug\mod_autoindex.so.manifest mt.exe -manifest .\Debug\mod_autoindex.so.manifest -outputresource:.\Debug\mod_autoindex.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_autoindex.dep") +!INCLUDE "mod_autoindex.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_autoindex.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_autoindex - Win32 Release" || "$(CFG)" == "mod_autoindex - Win32 Debug" + +!IF "$(CFG)" == "mod_autoindex - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\generators" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\generators" + +!ELSEIF "$(CFG)" == "mod_autoindex - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\generators" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\generators" + +!ENDIF + +!IF "$(CFG)" == "mod_autoindex - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\generators" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\generators" + +!ELSEIF "$(CFG)" == "mod_autoindex - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\generators" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\generators" + +!ENDIF + +!IF "$(CFG)" == "mod_autoindex - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\generators" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\generators" + +!ELSEIF "$(CFG)" == "mod_autoindex - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\generators" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\generators" + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_autoindex - Win32 Release" + + +"$(INTDIR)\mod_autoindex.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_autoindex.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_autoindex.so" /d LONG_NAME="autoindex_module for Apache" $(SOURCE) + + +!ELSEIF "$(CFG)" == "mod_autoindex - Win32 Debug" + + +"$(INTDIR)\mod_autoindex.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_autoindex.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_autoindex.so" /d LONG_NAME="autoindex_module for Apache" $(SOURCE) + + +!ENDIF + +SOURCE=.\mod_autoindex.c + +"$(INTDIR)\mod_autoindex.obj" : $(SOURCE) "$(INTDIR)" + + + +!ENDIF + diff --git a/modules/generators/mod_cgi.c b/modules/generators/mod_cgi.c new file mode 100644 index 0000000..8c4a2c6 --- /dev/null +++ b/modules/generators/mod_cgi.c @@ -0,0 +1,1281 @@ +/* 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_script: keeps all script-related ramblings together. + * + * Compliant to CGI/1.1 spec + * + * Adapted by rst from original NCSA code by Rob McCool + * + * This modules uses a httpd core function (ap_add_common_vars) to add some new env vars, + * like REDIRECT_URL and REDIRECT_QUERY_STRING for custom error responses and DOCUMENT_ROOT. + * It also adds SERVER_ADMIN - useful for scripts to know who to mail when they fail. + * + */ + +#include "apr.h" +#include "apr_strings.h" +#include "apr_thread_proc.h" /* for RLIMIT stuff */ +#include "apr_optional.h" +#include "apr_buckets.h" +#include "apr_lib.h" +#include "apr_poll.h" + +#define APR_WANT_STRFUNC +#define APR_WANT_MEMFUNC +#include "apr_want.h" + +#include "util_filter.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 "ap_mpm.h" +#include "mod_core.h" +#include "mod_cgi.h" + +#if APR_HAVE_STRUCT_RLIMIT +#if defined (RLIMIT_CPU) || defined (RLIMIT_NPROC) || defined (RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) +#define AP_CGI_USE_RLIMIT +#endif +#endif + +module AP_MODULE_DECLARE_DATA cgi_module; + +static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *cgi_pfn_reg_with_ssi; +static APR_OPTIONAL_FN_TYPE(ap_ssi_get_tag_and_value) *cgi_pfn_gtv; +static APR_OPTIONAL_FN_TYPE(ap_ssi_parse_string) *cgi_pfn_ps; +static APR_OPTIONAL_FN_TYPE(ap_cgi_build_command) *cgi_build_command; + +/* Read and discard the data in the brigade produced by a CGI script */ +static void discard_script_output(apr_bucket_brigade *bb); + +/* KLUDGE --- for back-combatibility, we don't have to check ExecCGI + * in ScriptAliased directories, which means we need to know if this + * request came through ScriptAlias or not... so the Alias module + * leaves a note for us. + */ + +static int is_scriptaliased(request_rec *r) +{ + const char *t = apr_table_get(r->notes, "alias-forced-type"); + return t && (!strcasecmp(t, "cgi-script")); +} + +/* Configuration stuff */ + +#define DEFAULT_LOGBYTES 10385760 +#define DEFAULT_BUFBYTES 1024 + +typedef struct { + const char *logname; + long logbytes; + apr_size_t bufbytes; +} cgi_server_conf; + +static void *create_cgi_config(apr_pool_t *p, server_rec *s) +{ + cgi_server_conf *c = + (cgi_server_conf *) apr_pcalloc(p, sizeof(cgi_server_conf)); + + c->logname = NULL; + c->logbytes = DEFAULT_LOGBYTES; + c->bufbytes = DEFAULT_BUFBYTES; + + return c; +} + +static void *merge_cgi_config(apr_pool_t *p, void *basev, void *overridesv) +{ + cgi_server_conf *base = (cgi_server_conf *) basev, + *overrides = (cgi_server_conf *) overridesv; + + return overrides->logname ? overrides : base; +} + +static const char *set_scriptlog(cmd_parms *cmd, void *dummy, const char *arg) +{ + server_rec *s = cmd->server; + cgi_server_conf *conf = ap_get_module_config(s->module_config, + &cgi_module); + + conf->logname = ap_server_root_relative(cmd->pool, arg); + + if (!conf->logname) { + return apr_pstrcat(cmd->pool, "Invalid ScriptLog path ", + arg, NULL); + } + + return NULL; +} + +static const char *set_scriptlog_length(cmd_parms *cmd, void *dummy, + const char *arg) +{ + server_rec *s = cmd->server; + cgi_server_conf *conf = ap_get_module_config(s->module_config, + &cgi_module); + + conf->logbytes = atol(arg); + return NULL; +} + +static const char *set_scriptlog_buffer(cmd_parms *cmd, void *dummy, + const char *arg) +{ + server_rec *s = cmd->server; + cgi_server_conf *conf = ap_get_module_config(s->module_config, + &cgi_module); + + conf->bufbytes = atoi(arg); + return NULL; +} + +static const command_rec cgi_cmds[] = +{ +AP_INIT_TAKE1("ScriptLog", set_scriptlog, NULL, RSRC_CONF, + "the name of a log for script debugging info"), +AP_INIT_TAKE1("ScriptLogLength", set_scriptlog_length, NULL, RSRC_CONF, + "the maximum length (in bytes) of the script debug log"), +AP_INIT_TAKE1("ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF, + "the maximum size (in bytes) to record of a POST request"), + {NULL} +}; + +static int log_scripterror(request_rec *r, cgi_server_conf * conf, int ret, + apr_status_t rv, char *logno, char *error) +{ + apr_file_t *f = NULL; + apr_finfo_t finfo; + char time_str[APR_CTIME_LEN]; + int log_flags = rv ? APLOG_ERR : APLOG_ERR; + + /* Intentional no APLOGNO */ + /* Callee provides APLOGNO in error text */ + ap_log_rerror(APLOG_MARK, log_flags, rv, r, + "%s%s: %s", logno ? logno : "", error, r->filename); + + /* XXX Very expensive mainline case! Open, then getfileinfo! */ + if (!conf->logname || + ((apr_stat(&finfo, conf->logname, + APR_FINFO_SIZE, r->pool) == APR_SUCCESS) && + (finfo.size > conf->logbytes)) || + (apr_file_open(&f, conf->logname, + APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT, + r->pool) != APR_SUCCESS)) { + return ret; + } + + /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */ + apr_ctime(time_str, apr_time_now()); + apr_file_printf(f, "%%%% [%s] %s %s%s%s %s\n", time_str, r->method, r->uri, + r->args ? "?" : "", r->args ? r->args : "", r->protocol); + /* "%% 500 /usr/local/apache/cgi-bin */ + apr_file_printf(f, "%%%% %d %s\n", ret, r->filename); + + apr_file_printf(f, "%%error\n%s\n", error); + + apr_file_close(f); + return ret; +} + +/* Soak up stderr from a script and redirect it to the error log. + */ +static apr_status_t log_script_err(request_rec *r, apr_file_t *script_err) +{ + char argsbuffer[HUGE_STRING_LEN]; + char *newline; + apr_status_t rv; + cgi_server_conf *conf = ap_get_module_config(r->server->module_config, &cgi_module); + + while ((rv = apr_file_gets(argsbuffer, HUGE_STRING_LEN, + script_err)) == APR_SUCCESS) { + newline = strchr(argsbuffer, '\n'); + if (newline) { + *newline = '\0'; + } + log_scripterror(r, conf, r->status, 0, APLOGNO(01215), argsbuffer); + } + + return rv; +} + +static int log_script(request_rec *r, cgi_server_conf * conf, int ret, + char *dbuf, const char *sbuf, apr_bucket_brigade *bb, + apr_file_t *script_err) +{ + const apr_array_header_t *hdrs_arr = apr_table_elts(r->headers_in); + const apr_table_entry_t *hdrs = (const apr_table_entry_t *) hdrs_arr->elts; + char argsbuffer[HUGE_STRING_LEN]; + apr_file_t *f = NULL; + apr_bucket *e; + const char *buf; + apr_size_t len; + apr_status_t rv; + int first; + int i; + apr_finfo_t finfo; + char time_str[APR_CTIME_LEN]; + + /* XXX Very expensive mainline case! Open, then getfileinfo! */ + if (!conf->logname || + ((apr_stat(&finfo, conf->logname, + APR_FINFO_SIZE, r->pool) == APR_SUCCESS) && + (finfo.size > conf->logbytes)) || + (apr_file_open(&f, conf->logname, + APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT, + r->pool) != APR_SUCCESS)) { + /* Soak up script output */ + discard_script_output(bb); + log_script_err(r, script_err); + return ret; + } + + /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */ + apr_ctime(time_str, apr_time_now()); + apr_file_printf(f, "%%%% [%s] %s %s%s%s %s\n", time_str, r->method, r->uri, + r->args ? "?" : "", r->args ? r->args : "", r->protocol); + /* "%% 500 /usr/local/apache/cgi-bin" */ + apr_file_printf(f, "%%%% %d %s\n", ret, r->filename); + + apr_file_puts("%request\n", f); + for (i = 0; i < hdrs_arr->nelts; ++i) { + if (!hdrs[i].key) + continue; + apr_file_printf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val); + } + if ((r->method_number == M_POST || r->method_number == M_PUT) && + *dbuf) { + apr_file_printf(f, "\n%s\n", dbuf); + } + + apr_file_puts("%response\n", f); + hdrs_arr = apr_table_elts(r->err_headers_out); + hdrs = (const apr_table_entry_t *) hdrs_arr->elts; + + for (i = 0; i < hdrs_arr->nelts; ++i) { + if (!hdrs[i].key) + continue; + apr_file_printf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val); + } + + if (sbuf && *sbuf) + apr_file_printf(f, "%s\n", sbuf); + + first = 1; + for (e = APR_BRIGADE_FIRST(bb); + e != APR_BRIGADE_SENTINEL(bb); + e = APR_BUCKET_NEXT(e)) + { + if (APR_BUCKET_IS_EOS(e)) { + break; + } + rv = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ); + if (rv != APR_SUCCESS || (len == 0)) { + break; + } + if (first) { + apr_file_puts("%stdout\n", f); + first = 0; + } + apr_file_write(f, buf, &len); + apr_file_puts("\n", f); + } + + if (apr_file_gets(argsbuffer, HUGE_STRING_LEN, script_err) == APR_SUCCESS) { + apr_file_puts("%stderr\n", f); + apr_file_puts(argsbuffer, f); + while (apr_file_gets(argsbuffer, HUGE_STRING_LEN, + script_err) == APR_SUCCESS) { + apr_file_puts(argsbuffer, f); + } + apr_file_puts("\n", f); + } + + apr_brigade_destroy(bb); + apr_file_close(script_err); + + apr_file_close(f); + return ret; +} + + +/* This is the special environment used for running the "exec cmd=" + * variety of SSI directives. + */ +static void add_ssi_vars(request_rec *r) +{ + apr_table_t *e = r->subprocess_env; + + if (r->path_info && r->path_info[0] != '\0') { + request_rec *pa_req; + + apr_table_setn(e, "PATH_INFO", ap_escape_shell_cmd(r->pool, + r->path_info)); + + pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), + r, NULL); + if (pa_req->filename) { + apr_table_setn(e, "PATH_TRANSLATED", + apr_pstrcat(r->pool, pa_req->filename, + pa_req->path_info, NULL)); + } + ap_destroy_sub_req(pa_req); + } + + if (r->args) { + char *arg_copy = apr_pstrdup(r->pool, r->args); + + apr_table_setn(e, "QUERY_STRING", r->args); + ap_unescape_url(arg_copy); + apr_table_setn(e, "QUERY_STRING_UNESCAPED", + ap_escape_shell_cmd(r->pool, arg_copy)); + } +} + +static void cgi_child_errfn(apr_pool_t *pool, apr_status_t err, + const char *description) +{ + apr_file_t *stderr_log; + + apr_file_open_stderr(&stderr_log, pool); + /* Escape the logged string because it may be something that + * came in over the network. + */ + apr_file_printf(stderr_log, + "(%d)%pm: %s\n", + err, + &err, +#ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED + ap_escape_logitem(pool, +#endif + description +#ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED + ) +#endif + ); +} + +static apr_status_t run_cgi_child(apr_file_t **script_out, + apr_file_t **script_in, + apr_file_t **script_err, + const char *command, + const char * const argv[], + request_rec *r, + apr_pool_t *p, + cgi_exec_info_t *e_info) +{ + const char * const *env; + apr_procattr_t *procattr; + apr_proc_t *procnew; + apr_status_t rc = APR_SUCCESS; + +#ifdef AP_CGI_USE_RLIMIT + core_dir_config *conf = ap_get_core_module_config(r->per_dir_config); +#endif + +#ifdef DEBUG_CGI +#ifdef OS2 + /* Under OS/2 need to use device con. */ + FILE *dbg = fopen("con", "w"); +#else + FILE *dbg = fopen("/dev/tty", "w"); +#endif + int i; +#endif + + RAISE_SIGSTOP(CGI_CHILD); +#ifdef DEBUG_CGI + fprintf(dbg, "Attempting to exec %s as CGI child (argv0 = %s)\n", + r->filename, argv[0]); +#endif + + env = (const char * const *)ap_create_environment(p, r->subprocess_env); + +#ifdef DEBUG_CGI + fprintf(dbg, "Environment: \n"); + for (i = 0; env[i]; ++i) + fprintf(dbg, "'%s'\n", env[i]); + fclose(dbg); +#endif + + /* Transmute ourselves into the script. + * NB only ISINDEX scripts get decoded arguments. + */ + if (((rc = apr_procattr_create(&procattr, p)) != APR_SUCCESS) || + ((rc = apr_procattr_io_set(procattr, + e_info->in_pipe, + e_info->out_pipe, + e_info->err_pipe)) != APR_SUCCESS) || + ((rc = apr_procattr_dir_set(procattr, + ap_make_dirstr_parent(r->pool, + r->filename))) != APR_SUCCESS) || +#if defined(RLIMIT_CPU) && defined(AP_CGI_USE_RLIMIT) + ((rc = apr_procattr_limit_set(procattr, APR_LIMIT_CPU, + conf->limit_cpu)) != APR_SUCCESS) || +#endif +#if defined(AP_CGI_USE_RLIMIT) && (defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)) + ((rc = apr_procattr_limit_set(procattr, APR_LIMIT_MEM, + conf->limit_mem)) != APR_SUCCESS) || +#endif +#if defined(RLIMIT_NPROC) && defined(AP_CGI_USE_RLIMIT) + ((rc = apr_procattr_limit_set(procattr, APR_LIMIT_NPROC, + conf->limit_nproc)) != APR_SUCCESS) || +#endif + ((rc = apr_procattr_cmdtype_set(procattr, + e_info->cmd_type)) != APR_SUCCESS) || + + ((rc = apr_procattr_detach_set(procattr, + e_info->detached)) != APR_SUCCESS) || + ((rc = apr_procattr_addrspace_set(procattr, + e_info->addrspace)) != APR_SUCCESS) || + ((rc = apr_procattr_child_errfn_set(procattr, cgi_child_errfn)) != APR_SUCCESS)) { + /* Something bad happened, tell the world. */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(01216) + "couldn't set child process attributes: %s", r->filename); + } + else { + procnew = apr_pcalloc(p, sizeof(*procnew)); + rc = ap_os_create_privileged_process(r, procnew, command, argv, env, + procattr, p); + + if (rc != APR_SUCCESS) { + /* Bad things happened. Everyone should have cleaned up. */ + /* Intentional no APLOGNO */ + ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_TOCLIENT, rc, r, + "couldn't create child process: %d: %s", rc, + apr_filepath_name_get(r->filename)); + } + else { + apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT); + + *script_in = procnew->out; + if (!*script_in) + return APR_EBADF; + apr_file_pipe_timeout_set(*script_in, r->server->timeout); + + if (e_info->prog_type == RUN_AS_CGI) { + *script_out = procnew->in; + if (!*script_out) + return APR_EBADF; + apr_file_pipe_timeout_set(*script_out, r->server->timeout); + + *script_err = procnew->err; + if (!*script_err) + return APR_EBADF; + apr_file_pipe_timeout_set(*script_err, r->server->timeout); + } + } + } + return (rc); +} + + +static apr_status_t default_build_command(const char **cmd, const char ***argv, + request_rec *r, apr_pool_t *p, + cgi_exec_info_t *e_info) +{ + int numwords, x, idx; + char *w; + const char *args = NULL; + + if (e_info->process_cgi) { + *cmd = r->filename; + /* Do not process r->args if they contain an '=' assignment + */ + if (r->args && r->args[0] && !ap_strchr_c(r->args, '=')) { + args = r->args; + } + } + + if (!args) { + numwords = 1; + } + else { + /* count the number of keywords */ + for (x = 0, numwords = 2; args[x]; x++) { + if (args[x] == '+') { + ++numwords; + } + } + } + /* Everything is - 1 to account for the first parameter + * which is the program name. + */ + if (numwords > APACHE_ARG_MAX - 1) { + numwords = APACHE_ARG_MAX - 1; /* Truncate args to prevent overrun */ + } + *argv = apr_palloc(p, (numwords + 2) * sizeof(char *)); + (*argv)[0] = *cmd; + for (x = 1, idx = 1; x < numwords; x++) { + w = ap_getword_nulls(p, &args, '+'); + ap_unescape_url(w); + (*argv)[idx++] = ap_escape_shell_cmd(p, w); + } + (*argv)[idx] = NULL; + + return APR_SUCCESS; +} + +static void discard_script_output(apr_bucket_brigade *bb) +{ + apr_bucket *e; + const char *buf; + apr_size_t len; + apr_status_t rv; + + for (e = APR_BRIGADE_FIRST(bb); + e != APR_BRIGADE_SENTINEL(bb); + e = APR_BUCKET_NEXT(e)) + { + if (APR_BUCKET_IS_EOS(e)) { + break; + } + rv = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ); + if (rv != APR_SUCCESS) { + break; + } + } +} + +#if APR_FILES_AS_SOCKETS + +/* A CGI bucket type is needed to catch any output to stderr from the + * script; see PR 22030. */ +static const apr_bucket_type_t bucket_type_cgi; + +struct cgi_bucket_data { + apr_pollset_t *pollset; + request_rec *r; +}; + +/* Create a CGI bucket using pipes from script stdout 'out' + * and stderr 'err', for request 'r'. */ +static apr_bucket *cgi_bucket_create(request_rec *r, + apr_file_t *out, apr_file_t *err, + apr_bucket_alloc_t *list) +{ + apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); + apr_status_t rv; + apr_pollfd_t fd; + struct cgi_bucket_data *data = apr_palloc(r->pool, sizeof *data); + + APR_BUCKET_INIT(b); + b->free = apr_bucket_free; + b->list = list; + b->type = &bucket_type_cgi; + b->length = (apr_size_t)(-1); + b->start = -1; + + /* Create the pollset */ + rv = apr_pollset_create(&data->pollset, 2, r->pool, 0); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01217) + "apr_pollset_create(); check system or user limits"); + return NULL; + } + + fd.desc_type = APR_POLL_FILE; + fd.reqevents = APR_POLLIN; + fd.p = r->pool; + fd.desc.f = out; /* script's stdout */ + fd.client_data = (void *)1; + rv = apr_pollset_add(data->pollset, &fd); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01218) + "apr_pollset_add(); check system or user limits"); + return NULL; + } + + fd.desc.f = err; /* script's stderr */ + fd.client_data = (void *)2; + rv = apr_pollset_add(data->pollset, &fd); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01219) + "apr_pollset_add(); check system or user limits"); + return NULL; + } + + data->r = r; + b->data = data; + return b; +} + +/* Create a duplicate CGI bucket using given bucket data */ +static apr_bucket *cgi_bucket_dup(struct cgi_bucket_data *data, + apr_bucket_alloc_t *list) +{ + apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); + APR_BUCKET_INIT(b); + b->free = apr_bucket_free; + b->list = list; + b->type = &bucket_type_cgi; + b->length = (apr_size_t)(-1); + b->start = -1; + b->data = data; + return b; +} + +/* Handle stdout from CGI child. Duplicate of logic from the _read + * method of the real APR pipe bucket implementation. */ +static apr_status_t cgi_read_stdout(apr_bucket *a, apr_file_t *out, + const char **str, apr_size_t *len) +{ + char *buf; + apr_status_t rv; + + *str = NULL; + *len = APR_BUCKET_BUFF_SIZE; + buf = apr_bucket_alloc(*len, a->list); /* XXX: check for failure? */ + + rv = apr_file_read(out, buf, len); + + if (rv != APR_SUCCESS && rv != APR_EOF) { + apr_bucket_free(buf); + return rv; + } + + if (*len > 0) { + struct cgi_bucket_data *data = a->data; + apr_bucket_heap *h; + + /* Change the current bucket to refer to what we read */ + a = apr_bucket_heap_make(a, buf, *len, apr_bucket_free); + h = a->data; + h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */ + *str = buf; + APR_BUCKET_INSERT_AFTER(a, cgi_bucket_dup(data, a->list)); + } + else { + apr_bucket_free(buf); + a = apr_bucket_immortal_make(a, "", 0); + *str = a->data; + } + return rv; +} + +/* Read method of CGI bucket: polls on stderr and stdout of the child, + * sending any stderr output immediately away to the error log. */ +static apr_status_t cgi_bucket_read(apr_bucket *b, const char **str, + apr_size_t *len, apr_read_type_e block) +{ + struct cgi_bucket_data *data = b->data; + apr_interval_time_t timeout; + apr_status_t rv; + int gotdata = 0; + + timeout = block == APR_NONBLOCK_READ ? 0 : data->r->server->timeout; + + do { + const apr_pollfd_t *results; + apr_int32_t num; + + rv = apr_pollset_poll(data->pollset, timeout, &num, &results); + if (APR_STATUS_IS_TIMEUP(rv)) { + if (timeout) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, data->r, APLOGNO(01220) + "Timeout waiting for output from CGI script %s", + data->r->filename); + return rv; + } + else { + return APR_EAGAIN; + } + } + else if (APR_STATUS_IS_EINTR(rv)) { + continue; + } + else if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, data->r, APLOGNO(01221) + "poll failed waiting for CGI child"); + return rv; + } + + for (; num; num--, results++) { + if (results[0].client_data == (void *)1) { + /* stdout */ + rv = cgi_read_stdout(b, results[0].desc.f, str, len); + if (APR_STATUS_IS_EOF(rv)) { + rv = APR_SUCCESS; + } + gotdata = 1; + } else { + /* stderr */ + apr_status_t rv2 = log_script_err(data->r, results[0].desc.f); + if (APR_STATUS_IS_EOF(rv2)) { + apr_pollset_remove(data->pollset, &results[0]); + } + } + } + + } while (!gotdata); + + return rv; +} + +static const apr_bucket_type_t bucket_type_cgi = { + "CGI", 5, APR_BUCKET_DATA, + apr_bucket_destroy_noop, + cgi_bucket_read, + apr_bucket_setaside_notimpl, + apr_bucket_split_notimpl, + apr_bucket_copy_notimpl +}; + +#endif + +static int cgi_handler(request_rec *r) +{ + int nph; + apr_size_t dbpos = 0; + const char *argv0; + const char *command; + const char **argv; + char *dbuf = NULL; + apr_file_t *script_out = NULL, *script_in = NULL, *script_err = NULL; + apr_bucket_brigade *bb; + apr_bucket *b; + int is_included; + int seen_eos, child_stopped_reading; + apr_pool_t *p; + cgi_server_conf *conf; + apr_status_t rv; + cgi_exec_info_t e_info; + conn_rec *c; + + if (strcmp(r->handler, CGI_MAGIC_TYPE) && strcmp(r->handler, "cgi-script")) { + return DECLINED; + } + + c = r->connection; + + is_included = !strcmp(r->protocol, "INCLUDED"); + + p = r->main ? r->main->pool : r->pool; + + argv0 = apr_filepath_name_get(r->filename); + nph = !(strncmp(argv0, "nph-", 4)); + conf = ap_get_module_config(r->server->module_config, &cgi_module); + + if (!(ap_allow_options(r) & OPT_EXECCGI) && !is_scriptaliased(r)) + return log_scripterror(r, conf, HTTP_FORBIDDEN, 0, APLOGNO(02809), + "Options ExecCGI is off in this directory"); + if (nph && is_included) + return log_scripterror(r, conf, HTTP_FORBIDDEN, 0, APLOGNO(02810), + "attempt to include NPH CGI script"); + + if (r->finfo.filetype == APR_NOFILE) + return log_scripterror(r, conf, HTTP_NOT_FOUND, 0, APLOGNO(02811), + "script not found or unable to stat"); + if (r->finfo.filetype == APR_DIR) + return log_scripterror(r, conf, HTTP_FORBIDDEN, 0, APLOGNO(02812), + "attempt to invoke directory as script"); + + if ((r->used_path_info == AP_REQ_REJECT_PATH_INFO) && + r->path_info && *r->path_info) + { + /* default to accept */ + return log_scripterror(r, conf, HTTP_NOT_FOUND, 0, APLOGNO(02813), + "AcceptPathInfo off disallows user's path"); + } +/* + if (!ap_suexec_enabled) { + if (!ap_can_exec(&r->finfo)) + return log_scripterror(r, conf, HTTP_FORBIDDEN, 0, APLOGNO(03194) + "file permissions deny server execution"); + } + +*/ + ap_add_common_vars(r); + ap_add_cgi_vars(r); + + e_info.process_cgi = 1; + e_info.cmd_type = APR_PROGRAM; + e_info.detached = 0; + e_info.in_pipe = APR_CHILD_BLOCK; + e_info.out_pipe = APR_CHILD_BLOCK; + e_info.err_pipe = APR_CHILD_BLOCK; + e_info.prog_type = RUN_AS_CGI; + e_info.bb = NULL; + e_info.ctx = NULL; + e_info.next = NULL; + e_info.addrspace = 0; + + /* build the command line */ + if ((rv = cgi_build_command(&command, &argv, r, p, &e_info)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01222) + "don't know how to spawn child process: %s", + r->filename); + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* run the script in its own process */ + if ((rv = run_cgi_child(&script_out, &script_in, &script_err, + command, argv, r, p, &e_info)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01223) + "couldn't spawn child process: %s", r->filename); + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* Transfer any put/post args, CERN style... + * Note that we already ignore SIGPIPE in the core server. + */ + bb = apr_brigade_create(r->pool, c->bucket_alloc); + seen_eos = 0; + child_stopped_reading = 0; + if (conf->logname) { + dbuf = apr_palloc(r->pool, conf->bufbytes + 1); + dbpos = 0; + } + do { + apr_bucket *bucket; + + rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, + APR_BLOCK_READ, HUGE_STRING_LEN); + + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01225) + "Error reading request entity data"); + return ap_map_http_request_error(rv, HTTP_BAD_REQUEST); + } + + for (bucket = APR_BRIGADE_FIRST(bb); + bucket != APR_BRIGADE_SENTINEL(bb); + bucket = APR_BUCKET_NEXT(bucket)) + { + const char *data; + apr_size_t len; + + if (APR_BUCKET_IS_EOS(bucket)) { + seen_eos = 1; + break; + } + + /* We can't do much with this. */ + if (APR_BUCKET_IS_FLUSH(bucket)) { + continue; + } + + /* If the child stopped, we still must read to EOS. */ + if (child_stopped_reading) { + continue; + } + + /* read */ + apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ); + + if (conf->logname && dbpos < conf->bufbytes) { + int cursize; + + if ((dbpos + len) > conf->bufbytes) { + cursize = conf->bufbytes - dbpos; + } + else { + cursize = len; + } + memcpy(dbuf + dbpos, data, cursize); + dbpos += cursize; + } + + /* Keep writing data to the child until done or too much time + * elapses with no progress or an error occurs. + */ + rv = apr_file_write_full(script_out, data, len, NULL); + + if (rv != APR_SUCCESS) { + /* silly script stopped reading, soak up remaining message */ + child_stopped_reading = 1; + } + } + apr_brigade_cleanup(bb); + } + while (!seen_eos); + + if (conf->logname) { + dbuf[dbpos] = '\0'; + } + /* Is this flush really needed? */ + apr_file_flush(script_out); + apr_file_close(script_out); + + AP_DEBUG_ASSERT(script_in != NULL); + +#if APR_FILES_AS_SOCKETS + apr_file_pipe_timeout_set(script_in, 0); + apr_file_pipe_timeout_set(script_err, 0); + + b = cgi_bucket_create(r, script_in, script_err, c->bucket_alloc); + if (b == NULL) + return HTTP_INTERNAL_SERVER_ERROR; +#else + b = apr_bucket_pipe_create(script_in, c->bucket_alloc); +#endif + APR_BRIGADE_INSERT_TAIL(bb, b); + b = apr_bucket_eos_create(c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, b); + + /* Handle script return... */ + if (!nph) { + const char *location; + char sbuf[MAX_STRING_LEN]; + int ret; + + if ((ret = ap_scan_script_header_err_brigade_ex(r, bb, sbuf, + APLOG_MODULE_INDEX))) + { + ret = log_script(r, conf, ret, dbuf, sbuf, bb, script_err); + + /* + * ret could be HTTP_NOT_MODIFIED in the case that the CGI script + * does not set an explicit status and ap_meets_conditions, which + * is called by ap_scan_script_header_err_brigade, detects that + * the conditions of the requests are met and the response is + * not modified. + * In this case set r->status and return OK in order to prevent + * running through the error processing stack as this would + * break with mod_cache, if the conditions had been set by + * mod_cache itself to validate a stale entity. + * BTW: We circumvent the error processing stack anyway if the + * CGI script set an explicit status code (whatever it is) and + * the only possible values for ret here are: + * + * HTTP_NOT_MODIFIED (set by ap_meets_conditions) + * HTTP_PRECONDITION_FAILED (set by ap_meets_conditions) + * HTTP_INTERNAL_SERVER_ERROR (if something went wrong during the + * processing of the response of the CGI script, e.g broken headers + * or a crashed CGI process). + */ + if (ret == HTTP_NOT_MODIFIED) { + r->status = ret; + return OK; + } + + return ret; + } + + location = apr_table_get(r->headers_out, "Location"); + + if (location && r->status == 200) { + /* For a redirect whether internal or not, discard any + * remaining stdout from the script, and log any remaining + * stderr output, as normal. */ + discard_script_output(bb); + apr_brigade_destroy(bb); + apr_file_pipe_timeout_set(script_err, r->server->timeout); + log_script_err(r, script_err); + } + + if (location && location[0] == '/' && r->status == 200) { + /* This redirect needs to be a GET no matter what the original + * method was. + */ + r->method = "GET"; + r->method_number = M_GET; + + /* We already read the message body (if any), so don't allow + * the redirected request to think it has one. We can ignore + * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR. + */ + apr_table_unset(r->headers_in, "Content-Length"); + + ap_internal_redirect_handler(location, r); + return OK; + } + else if (location && r->status == 200) { + /* XXX: Note that if a script wants to produce its own Redirect + * body, it now has to explicitly *say* "Status: 302" + */ + return HTTP_MOVED_TEMPORARILY; + } + + rv = ap_pass_brigade(r->output_filters, bb); + } + else /* nph */ { + struct ap_filter_t *cur; + + /* get rid of all filters up through protocol... since we + * haven't parsed off the headers, there is no way they can + * work + */ + + cur = r->proto_output_filters; + while (cur && cur->frec->ftype < AP_FTYPE_CONNECTION) { + cur = cur->next; + } + r->output_filters = r->proto_output_filters = cur; + + rv = ap_pass_brigade(r->output_filters, bb); + } + + /* don't soak up script output if errors occurred writing it + * out... otherwise, we prolong the life of the script when the + * connection drops or we stopped sending output for some other + * reason */ + if (rv == APR_SUCCESS && !r->connection->aborted) { + apr_file_pipe_timeout_set(script_err, r->server->timeout); + log_script_err(r, script_err); + } + + apr_file_close(script_err); + + return OK; /* NOT r->status, even if it has changed. */ +} + +/*============================================================================ + *============================================================================ + * This is the beginning of the cgi filter code moved from mod_include. This + * is the code required to handle the "exec" SSI directive. + *============================================================================ + *============================================================================*/ +static apr_status_t include_cgi(include_ctx_t *ctx, ap_filter_t *f, + apr_bucket_brigade *bb, char *s) +{ + request_rec *r = f->r; + request_rec *rr = ap_sub_req_lookup_uri(s, r, f->next); + int rr_status; + + if (rr->status != HTTP_OK) { + ap_destroy_sub_req(rr); + return APR_EGENERAL; + } + + /* No hardwired path info or query allowed */ + if ((rr->path_info && rr->path_info[0]) || rr->args) { + ap_destroy_sub_req(rr); + return APR_EGENERAL; + } + if (rr->finfo.filetype != APR_REG) { + ap_destroy_sub_req(rr); + return APR_EGENERAL; + } + + /* Script gets parameters of the *document*, for back compatibility */ + rr->path_info = r->path_info; /* hard to get right; see mod_cgi.c */ + rr->args = r->args; + + /* Force sub_req to be treated as a CGI request, even if ordinary + * typing rules would have called it something else. + */ + ap_set_content_type(rr, CGI_MAGIC_TYPE); + + /* Run it. */ + rr_status = ap_run_sub_req(rr); + if (ap_is_HTTP_REDIRECT(rr_status)) { + const char *location = apr_table_get(rr->headers_out, "Location"); + + if (location) { + char *buffer; + + location = ap_escape_html(rr->pool, location); + buffer = apr_pstrcat(ctx->pool, "<a href=\"", location, "\">", + location, "</a>", NULL); + + APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(buffer, + strlen(buffer), ctx->pool, + f->c->bucket_alloc)); + } + } + + ap_destroy_sub_req(rr); + + return APR_SUCCESS; +} + +static apr_status_t include_cmd(include_ctx_t *ctx, ap_filter_t *f, + apr_bucket_brigade *bb, const char *command) +{ + cgi_exec_info_t e_info; + const char **argv; + apr_file_t *script_out = NULL, *script_in = NULL, *script_err = NULL; + apr_status_t rv; + request_rec *r = f->r; + + add_ssi_vars(r); + + e_info.process_cgi = 0; + e_info.cmd_type = APR_SHELLCMD; + e_info.detached = 0; + e_info.in_pipe = APR_NO_PIPE; + e_info.out_pipe = APR_FULL_BLOCK; + e_info.err_pipe = APR_NO_PIPE; + e_info.prog_type = RUN_AS_SSI; + e_info.bb = &bb; + e_info.ctx = ctx; + e_info.next = f->next; + e_info.addrspace = 0; + + if ((rv = cgi_build_command(&command, &argv, r, r->pool, + &e_info)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01226) + "don't know how to spawn cmd child process: %s", + r->filename); + return rv; + } + + /* run the script in its own process */ + if ((rv = run_cgi_child(&script_out, &script_in, &script_err, + command, argv, r, r->pool, + &e_info)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01227) + "couldn't spawn child process: %s", r->filename); + return rv; + } + + APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pipe_create(script_in, + f->c->bucket_alloc)); + ctx->flush_now = 1; + + /* We can't close the pipe here, because we may return before the + * full CGI has been sent to the network. That's okay though, + * because we can rely on the pool to close the pipe for us. + */ + return APR_SUCCESS; +} + +static apr_status_t handle_exec(include_ctx_t *ctx, ap_filter_t *f, + apr_bucket_brigade *bb) +{ + char *tag = NULL; + char *tag_val = NULL; + request_rec *r = f->r; + char *file = r->filename; + char parsed_string[MAX_STRING_LEN]; + + if (!ctx->argc) { + ap_log_rerror(APLOG_MARK, + (ctx->flags & SSI_FLAG_PRINTING) + ? APLOG_ERR : APLOG_WARNING, + 0, r, APLOGNO(03195) + "missing argument for exec element in %s", r->filename); + } + + if (!(ctx->flags & SSI_FLAG_PRINTING)) { + return APR_SUCCESS; + } + + if (!ctx->argc) { + SSI_CREATE_ERROR_BUCKET(ctx, f, bb); + return APR_SUCCESS; + } + + if (ctx->flags & SSI_FLAG_NO_EXEC) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01228) "exec used but not allowed " + "in %s", r->filename); + SSI_CREATE_ERROR_BUCKET(ctx, f, bb); + return APR_SUCCESS; + } + + while (1) { + cgi_pfn_gtv(ctx, &tag, &tag_val, SSI_VALUE_DECODED); + if (!tag || !tag_val) { + break; + } + + if (!strcmp(tag, "cmd")) { + apr_status_t rv; + + cgi_pfn_ps(ctx, tag_val, parsed_string, sizeof(parsed_string), + SSI_EXPAND_LEAVE_NAME); + + rv = include_cmd(ctx, f, bb, parsed_string); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01229) "execution failure " + "for parameter \"%s\" to tag exec in file %s", + tag, r->filename); + SSI_CREATE_ERROR_BUCKET(ctx, f, bb); + break; + } + } + else if (!strcmp(tag, "cgi")) { + apr_status_t rv; + + cgi_pfn_ps(ctx, tag_val, parsed_string, sizeof(parsed_string), + SSI_EXPAND_DROP_NAME); + + rv = include_cgi(ctx, f, bb, parsed_string); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01230) "invalid CGI ref " + "\"%s\" in %s", tag_val, file); + SSI_CREATE_ERROR_BUCKET(ctx, f, bb); + break; + } + } + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01231) "unknown parameter " + "\"%s\" to tag exec in %s", tag, file); + SSI_CREATE_ERROR_BUCKET(ctx, f, bb); + break; + } + } + + return APR_SUCCESS; +} + + +/*============================================================================ + *============================================================================ + * This is the end of the cgi filter code moved from mod_include. + *============================================================================ + *============================================================================*/ + + +static int cgi_post_config(apr_pool_t *p, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *s) +{ + cgi_pfn_reg_with_ssi = APR_RETRIEVE_OPTIONAL_FN(ap_register_include_handler); + cgi_pfn_gtv = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_get_tag_and_value); + cgi_pfn_ps = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_parse_string); + + if ((cgi_pfn_reg_with_ssi) && (cgi_pfn_gtv) && (cgi_pfn_ps)) { + /* Required by mod_include filter. This is how mod_cgi registers + * with mod_include to provide processing of the exec directive. + */ + cgi_pfn_reg_with_ssi("exec", handle_exec); + } + + /* This is the means by which unusual (non-unix) os's may find alternate + * means to run a given command (e.g. shebang/registry parsing on Win32) + */ + cgi_build_command = APR_RETRIEVE_OPTIONAL_FN(ap_cgi_build_command); + if (!cgi_build_command) { + cgi_build_command = default_build_command; + } + return OK; +} + +static void register_hooks(apr_pool_t *p) +{ + static const char * const aszPre[] = { "mod_include.c", NULL }; + ap_hook_handler(cgi_handler, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_post_config(cgi_post_config, aszPre, NULL, APR_HOOK_REALLY_FIRST); +} + +AP_DECLARE_MODULE(cgi) = +{ + STANDARD20_MODULE_STUFF, + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + create_cgi_config, /* server config */ + merge_cgi_config, /* merge server config */ + cgi_cmds, /* command apr_table_t */ + register_hooks /* register hooks */ +}; diff --git a/modules/generators/mod_cgi.dep b/modules/generators/mod_cgi.dep new file mode 100644 index 0000000..3da4698 --- /dev/null +++ b/modules/generators/mod_cgi.dep @@ -0,0 +1,64 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_cgi.mak + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + + +.\mod_cgi.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_mpm.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\httpd.h"\ + "..\..\include\mod_core.h"\ + "..\..\include\mod_include.h"\ + "..\..\include\os.h"\ + "..\..\include\scoreboard.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_script.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + ".\mod_cgi.h"\ + diff --git a/modules/generators/mod_cgi.dsp b/modules/generators/mod_cgi.dsp new file mode 100644 index 0000000..4fe58e0 --- /dev/null +++ b/modules/generators/mod_cgi.dsp @@ -0,0 +1,115 @@ +# Microsoft Developer Studio Project File - Name="mod_cgi" - 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_cgi - 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_cgi.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_cgi.mak" CFG="mod_cgi - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_cgi - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_cgi - 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_cgi - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_cgi_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_cgi.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_cgi.so" /d LONG_NAME="cgi_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:".\Release\mod_cgi.so" /base:@..\..\os\win32\BaseAddr.ref,mod_cgi.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_cgi.so" /base:@..\..\os\win32\BaseAddr.ref,mod_cgi.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_cgi.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_cgi - 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_cgi_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_cgi.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_cgi.so" /d LONG_NAME="cgi_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_cgi.so" /base:@..\..\os\win32\BaseAddr.ref,mod_cgi.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_cgi.so" /base:@..\..\os\win32\BaseAddr.ref,mod_cgi.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_cgi.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_cgi - Win32 Release" +# Name "mod_cgi - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_cgi.c +# End Source File +# Begin Source File + +SOURCE=.\mod_cgi.h +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/generators/mod_cgi.exp b/modules/generators/mod_cgi.exp new file mode 100644 index 0000000..96ea0c2 --- /dev/null +++ b/modules/generators/mod_cgi.exp @@ -0,0 +1 @@ +cgi_module diff --git a/modules/generators/mod_cgi.h b/modules/generators/mod_cgi.h new file mode 100644 index 0000000..424c6f2 --- /dev/null +++ b/modules/generators/mod_cgi.h @@ -0,0 +1,67 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file mod_cgi.h + * @brief CGI Script Execution Extension Module for Apache + * + * @defgroup MOD_CGI mod_cgi + * @ingroup APACHE_MODS + * @{ + */ + +#ifndef _MOD_CGI_H +#define _MOD_CGI_H 1 + +#include "mod_include.h" + +typedef enum {RUN_AS_SSI, RUN_AS_CGI} prog_types; + +typedef struct { + apr_int32_t in_pipe; + apr_int32_t out_pipe; + apr_int32_t err_pipe; + int process_cgi; + apr_cmdtype_e cmd_type; + apr_int32_t detached; + prog_types prog_type; + apr_bucket_brigade **bb; + include_ctx_t *ctx; + ap_filter_t *next; + apr_int32_t addrspace; +} cgi_exec_info_t; + +/** + * Registerable optional function to override CGI behavior; + * Reprocess the command and arguments to execute the given CGI script. + * @param cmd Pointer to the command to execute (may be overridden) + * @param argv Pointer to the arguments to pass (may be overridden) + * @param r The current request + * @param p The pool to allocate correct cmd/argv elements within. + * @param e_info pass e_info.cmd_type (Set to APR_SHELLCMD or APR_PROGRAM on entry) + and e_info.detached (Should the child start in detached state?) + * @remark This callback may be registered by the os-specific module + * to correct the command and arguments for apr_proc_create invocation + * on a given os. mod_cgi will call the function if registered. + */ +APR_DECLARE_OPTIONAL_FN(apr_status_t, ap_cgi_build_command, + (const char **cmd, const char ***argv, + request_rec *r, apr_pool_t *p, + cgi_exec_info_t *e_info)); + +#endif /* _MOD_CGI_H */ +/** @} */ + diff --git a/modules/generators/mod_cgi.mak b/modules/generators/mod_cgi.mak new file mode 100644 index 0000000..bd5e7e7 --- /dev/null +++ b/modules/generators/mod_cgi.mak @@ -0,0 +1,353 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_cgi.dsp +!IF "$(CFG)" == "" +CFG=mod_cgi - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_cgi - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_cgi - Win32 Release" && "$(CFG)" != "mod_cgi - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_cgi.mak" CFG="mod_cgi - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_cgi - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_cgi - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_cgi - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_cgi.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_cgi.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_cgi.obj" + -@erase "$(INTDIR)\mod_cgi.res" + -@erase "$(INTDIR)\mod_cgi_src.idb" + -@erase "$(INTDIR)\mod_cgi_src.pdb" + -@erase "$(OUTDIR)\mod_cgi.exp" + -@erase "$(OUTDIR)\mod_cgi.lib" + -@erase "$(OUTDIR)\mod_cgi.pdb" + -@erase "$(OUTDIR)\mod_cgi.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_cgi_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_cgi.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_cgi.so" /d LONG_NAME="cgi_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_cgi.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_cgi.pdb" /debug /out:"$(OUTDIR)\mod_cgi.so" /implib:"$(OUTDIR)\mod_cgi.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_cgi.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_cgi.obj" \ + "$(INTDIR)\mod_cgi.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" + +"$(OUTDIR)\mod_cgi.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_cgi.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_cgi.so" + if exist .\Release\mod_cgi.so.manifest mt.exe -manifest .\Release\mod_cgi.so.manifest -outputresource:.\Release\mod_cgi.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_cgi - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_cgi.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_cgi.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_cgi.obj" + -@erase "$(INTDIR)\mod_cgi.res" + -@erase "$(INTDIR)\mod_cgi_src.idb" + -@erase "$(INTDIR)\mod_cgi_src.pdb" + -@erase "$(OUTDIR)\mod_cgi.exp" + -@erase "$(OUTDIR)\mod_cgi.lib" + -@erase "$(OUTDIR)\mod_cgi.pdb" + -@erase "$(OUTDIR)\mod_cgi.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_cgi_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_cgi.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_cgi.so" /d LONG_NAME="cgi_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_cgi.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_cgi.pdb" /debug /out:"$(OUTDIR)\mod_cgi.so" /implib:"$(OUTDIR)\mod_cgi.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_cgi.so +LINK32_OBJS= \ + "$(INTDIR)\mod_cgi.obj" \ + "$(INTDIR)\mod_cgi.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" + +"$(OUTDIR)\mod_cgi.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_cgi.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_cgi.so" + if exist .\Debug\mod_cgi.so.manifest mt.exe -manifest .\Debug\mod_cgi.so.manifest -outputresource:.\Debug\mod_cgi.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_cgi.dep") +!INCLUDE "mod_cgi.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_cgi.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_cgi - Win32 Release" || "$(CFG)" == "mod_cgi - Win32 Debug" + +!IF "$(CFG)" == "mod_cgi - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\generators" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\generators" + +!ELSEIF "$(CFG)" == "mod_cgi - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\generators" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\generators" + +!ENDIF + +!IF "$(CFG)" == "mod_cgi - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\generators" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\generators" + +!ELSEIF "$(CFG)" == "mod_cgi - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\generators" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\generators" + +!ENDIF + +!IF "$(CFG)" == "mod_cgi - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\generators" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\generators" + +!ELSEIF "$(CFG)" == "mod_cgi - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\generators" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\generators" + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_cgi - Win32 Release" + + +"$(INTDIR)\mod_cgi.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_cgi.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_cgi.so" /d LONG_NAME="cgi_module for Apache" $(SOURCE) + + +!ELSEIF "$(CFG)" == "mod_cgi - Win32 Debug" + + +"$(INTDIR)\mod_cgi.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_cgi.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_cgi.so" /d LONG_NAME="cgi_module for Apache" $(SOURCE) + + +!ENDIF + +SOURCE=.\mod_cgi.c + +"$(INTDIR)\mod_cgi.obj" : $(SOURCE) "$(INTDIR)" + + + +!ENDIF + diff --git a/modules/generators/mod_cgid.c b/modules/generators/mod_cgid.c new file mode 100644 index 0000000..b827ed6 --- /dev/null +++ b/modules/generators/mod_cgid.c @@ -0,0 +1,1980 @@ +/* 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_script: keeps all script-related ramblings together. + * + * Compliant to cgi/1.1 spec + * + * Adapted by rst from original NCSA code by Rob McCool + * + * This modules uses a httpd core function (ap_add_common_vars) to add some new env vars, + * like REDIRECT_URL and REDIRECT_QUERY_STRING for custom error responses and DOCUMENT_ROOT. + * It also adds SERVER_ADMIN - useful for scripts to know who to mail when they fail. + * + */ + +#include "apr_lib.h" +#include "apr_strings.h" +#include "apr_general.h" +#include "apr_file_io.h" +#include "apr_portable.h" +#include "apr_buckets.h" +#include "apr_optional.h" +#include "apr_signal.h" + +#define APR_WANT_STRFUNC +#include "apr_want.h" + +#if APR_HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#if APR_HAVE_UNISTD_H +#include <unistd.h> +#endif +#if APR_HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#include "util_filter.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 "ap_mpm.h" +#include "mpm_common.h" +#include "mod_suexec.h" +#include "../filters/mod_include.h" + +#include "mod_core.h" + + +/* ### should be tossed in favor of APR */ +#include <sys/stat.h> +#include <sys/un.h> /* for sockaddr_un */ + +#if APR_HAVE_STRUCT_RLIMIT +#if defined (RLIMIT_CPU) || defined (RLIMIT_NPROC) || defined (RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) +#define AP_CGID_USE_RLIMIT +#endif +#endif + +module AP_MODULE_DECLARE_DATA cgid_module; + +static int cgid_start(apr_pool_t *p, server_rec *main_server, apr_proc_t *procnew); +static int cgid_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *main_server); +static int handle_exec(include_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *bb); + +static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *cgid_pfn_reg_with_ssi; +static APR_OPTIONAL_FN_TYPE(ap_ssi_get_tag_and_value) *cgid_pfn_gtv; +static APR_OPTIONAL_FN_TYPE(ap_ssi_parse_string) *cgid_pfn_ps; + +static apr_pool_t *pcgi = NULL; +static pid_t daemon_pid; +static int daemon_should_exit = 0; +static server_rec *root_server = NULL; +static apr_pool_t *root_pool = NULL; +static const char *sockname; +static struct sockaddr_un *server_addr; +static apr_socklen_t server_addr_len; +static pid_t parent_pid; +static ap_unix_identity_t empty_ugid = { (uid_t)-1, (gid_t)-1, -1 }; + +typedef struct { + apr_interval_time_t timeout; +} cgid_dirconf; + +/* The APR other-child API doesn't tell us how the daemon exited + * (SIGSEGV vs. exit(1)). The other-child maintenance function + * needs to decide whether to restart the daemon after a failure + * based on whether or not it exited due to a fatal startup error + * or something that happened at steady-state. This exit status + * is unlikely to collide with exit signals. + */ +#define DAEMON_STARTUP_ERROR 254 + +/* Read and discard the data in the brigade produced by a CGI script */ +static void discard_script_output(apr_bucket_brigade *bb); + +/* This doer will only ever be called when we are sure that we have + * a valid ugid. + */ +static ap_unix_identity_t *cgid_suexec_id_doer(const request_rec *r) +{ + return (ap_unix_identity_t *) + ap_get_module_config(r->request_config, &cgid_module); +} + +/* KLUDGE --- for back-combatibility, we don't have to check ExecCGI + * in ScriptAliased directories, which means we need to know if this + * request came through ScriptAlias or not... so the Alias module + * leaves a note for us. + */ + +static int is_scriptaliased(request_rec *r) +{ + const char *t = apr_table_get(r->notes, "alias-forced-type"); + return t && (!strcasecmp(t, "cgi-script")); +} + +/* Configuration stuff */ + +#define DEFAULT_LOGBYTES 10385760 +#define DEFAULT_BUFBYTES 1024 +#define DEFAULT_SOCKET "cgisock" + +#define CGI_REQ 1 +#define SSI_REQ 2 +#define GETPID_REQ 3 /* get the pid of script created for prior request */ + +#define ERRFN_USERDATA_KEY "CGIDCHILDERRFN" + +/* DEFAULT_CGID_LISTENBACKLOG controls the max depth on the unix socket's + * pending connection queue. If a bunch of cgi requests arrive at about + * the same time, connections from httpd threads/processes will back up + * in the queue while the cgid process slowly forks off a child to process + * each connection on the unix socket. If the queue is too short, the + * httpd process will get ECONNREFUSED when trying to connect. + */ +#ifndef DEFAULT_CGID_LISTENBACKLOG +#define DEFAULT_CGID_LISTENBACKLOG 100 +#endif + +/* DEFAULT_CONNECT_ATTEMPTS controls how many times we'll try to connect + * to the cgi daemon from the thread/process handling the cgi request. + * Generally we want to retry when we get ECONNREFUSED since it is + * probably because the listen queue is full. We need to try harder so + * the client doesn't see it as a 503 error. + * + * Set this to 0 to continually retry until the connect works or Apache + * terminates. + */ +#ifndef DEFAULT_CONNECT_ATTEMPTS +#define DEFAULT_CONNECT_ATTEMPTS 15 +#endif + +#ifndef DEFAULT_CONNECT_STARTUP_DELAY +#define DEFAULT_CONNECT_STARTUP_DELAY 60 +#endif + +typedef struct { + const char *logname; + long logbytes; + int bufbytes; +} cgid_server_conf; + +#ifdef AP_CGID_USE_RLIMIT +typedef struct { +#ifdef RLIMIT_CPU + int limit_cpu_set; + struct rlimit limit_cpu; +#endif +#if defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined(RLIMIT_AS) + int limit_mem_set; + struct rlimit limit_mem; +#endif +#ifdef RLIMIT_NPROC + int limit_nproc_set; + struct rlimit limit_nproc; +#endif + +} cgid_rlimit_t; +#endif + +typedef struct { + int req_type; /* request type (CGI_REQ, SSI_REQ, etc.) */ + unsigned long conn_id; /* connection id; daemon uses this as a hash value + * to find the script pid when it is time for that + * process to be cleaned up + */ + pid_t ppid; /* sanity check for config problems leading to + * wrong cgid socket use + */ + int env_count; + ap_unix_identity_t ugid; + apr_size_t filename_len; + apr_size_t argv0_len; + apr_size_t uri_len; + apr_size_t args_len; + int loglevel; /* to stuff in server_rec */ + +#ifdef AP_CGID_USE_RLIMIT + cgid_rlimit_t limits; +#endif +} cgid_req_t; + +/* This routine is called to create the argument list to be passed + * to the CGI script. When suexec is enabled, the suexec path, user, and + * group are the first three arguments to be passed; if not, all three + * must be NULL. The query info is split into separate arguments, where + * "+" is the separator between keyword arguments. + * + * Do not process the args if they containing an '=' assignment. + */ +static char **create_argv(apr_pool_t *p, char *path, char *user, char *group, + char *av0, const char *args) +{ + int x, numwords; + char **av; + char *w; + int idx = 0; + + if (!(*args) || ap_strchr_c(args, '=')) { + numwords = 0; + } + else { + /* count the number of keywords */ + + for (x = 0, numwords = 1; args[x]; x++) { + if (args[x] == '+') { + ++numwords; + } + } + } + + if (numwords > APACHE_ARG_MAX - 5) { + numwords = APACHE_ARG_MAX - 5; /* Truncate args to prevent overrun */ + } + av = (char **) apr_pcalloc(p, (numwords + 5) * sizeof(char *)); + + if (path) { + av[idx++] = path; + } + if (user) { + av[idx++] = user; + } + if (group) { + av[idx++] = group; + } + + av[idx++] = apr_pstrdup(p, av0); + + for (x = 1; x <= numwords; x++) { + w = ap_getword_nulls(p, &args, '+'); + ap_unescape_url(w); + av[idx++] = ap_escape_shell_cmd(p, w); + } + av[idx] = NULL; + return av; +} + +#if APR_HAS_OTHER_CHILD +static void cgid_maint(int reason, void *data, apr_wait_t status) +{ + apr_proc_t *proc = data; + int mpm_state; + int stopping; + + switch (reason) { + case APR_OC_REASON_DEATH: + apr_proc_other_child_unregister(data); + /* If apache is not terminating or restarting, + * restart the cgid daemon + */ + stopping = 1; /* if MPM doesn't support query, + * assume we shouldn't restart daemon + */ + if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state) == APR_SUCCESS && + mpm_state != AP_MPMQ_STOPPING) { + stopping = 0; + } + if (!stopping) { + if (status == DAEMON_STARTUP_ERROR) { + ap_log_error(APLOG_MARK, APLOG_CRIT, 0, ap_server_conf, APLOGNO(01238) + "cgid daemon failed to initialize"); + } + else { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(01239) + "cgid daemon process died, restarting"); + cgid_start(root_pool, root_server, proc); + } + } + break; + case APR_OC_REASON_RESTART: + /* don't do anything; server is stopping or restarting */ + apr_proc_other_child_unregister(data); + break; + case APR_OC_REASON_LOST: + /* Restart the child cgid daemon process */ + apr_proc_other_child_unregister(data); + cgid_start(root_pool, root_server, proc); + break; + case APR_OC_REASON_UNREGISTER: + /* we get here when pcgi is cleaned up; pcgi gets cleaned + * up when pconf gets cleaned up + */ + kill(proc->pid, SIGHUP); /* send signal to daemon telling it to die */ + + /* Remove the cgi socket, we must do it here in order to try and + * guarantee the same permissions as when the socket was created. + */ + if (unlink(sockname) < 0 && errno != ENOENT) { + ap_log_error(APLOG_MARK, APLOG_ERR, errno, ap_server_conf, APLOGNO(01240) + "Couldn't unlink unix domain socket %s", + sockname); + } + break; + } +} +#endif + +static apr_status_t close_unix_socket(void *thefd) +{ + int fd = (int)((long)thefd); + + return close(fd); +} + +/* deal with incomplete reads and signals + * assume you really have to read buf_size bytes + */ +static apr_status_t sock_read(int fd, void *vbuf, size_t buf_size) +{ + char *buf = vbuf; + int rc; + size_t bytes_read = 0; + + do { + do { + rc = read(fd, buf + bytes_read, buf_size - bytes_read); + } while (rc < 0 && errno == EINTR); + switch(rc) { + case -1: + return errno; + case 0: /* unexpected */ + return ECONNRESET; + default: + bytes_read += rc; + } + } while (bytes_read < buf_size); + + return APR_SUCCESS; +} + +/* deal with signals + */ +static apr_status_t sock_write(int fd, const void *buf, size_t buf_size) +{ + int rc; + + do { + rc = write(fd, buf, buf_size); + } while (rc < 0 && errno == EINTR); + if (rc < 0) { + return errno; + } + + return APR_SUCCESS; +} + +static apr_status_t sock_writev(int fd, request_rec *r, int count, ...) +{ + va_list ap; + int rc; + struct iovec *vec; + int i; + + vec = (struct iovec *)apr_palloc(r->pool, count * sizeof(struct iovec)); + va_start(ap, count); + for (i = 0; i < count; i++) { + vec[i].iov_base = va_arg(ap, caddr_t); + vec[i].iov_len = va_arg(ap, apr_size_t); + } + va_end(ap); + + do { + rc = writev(fd, vec, count); + } while (rc < 0 && errno == EINTR); + if (rc < 0) { + return errno; + } + + return APR_SUCCESS; +} + +static apr_status_t get_req(int fd, request_rec *r, char **argv0, char ***env, + cgid_req_t *req) +{ + int i; + char **environ; + core_request_config *temp_core; + void **rconf; + apr_status_t stat; + + r->server = apr_pcalloc(r->pool, sizeof(server_rec)); + + /* read the request header */ + stat = sock_read(fd, req, sizeof(*req)); + if (stat != APR_SUCCESS) { + return stat; + } + r->server->log.level = req->loglevel; + if (req->req_type == GETPID_REQ) { + /* no more data sent for this request */ + return APR_SUCCESS; + } + + /* handle module indexes and such */ + rconf = (void **)ap_create_request_config(r->pool); + + temp_core = (core_request_config *)apr_palloc(r->pool, sizeof(core_module)); + rconf[AP_CORE_MODULE_INDEX] = (void *)temp_core; + r->request_config = (ap_conf_vector_t *)rconf; + ap_set_module_config(r->request_config, &cgid_module, (void *)&req->ugid); + + /* Read the filename, argv0, uri, and args */ + r->filename = apr_pcalloc(r->pool, req->filename_len + 1); + *argv0 = apr_pcalloc(r->pool, req->argv0_len + 1); + r->uri = apr_pcalloc(r->pool, req->uri_len + 1); + if ((stat = sock_read(fd, r->filename, req->filename_len)) != APR_SUCCESS || + (stat = sock_read(fd, *argv0, req->argv0_len)) != APR_SUCCESS || + (stat = sock_read(fd, r->uri, req->uri_len)) != APR_SUCCESS) { + return stat; + } + + r->args = apr_pcalloc(r->pool, req->args_len + 1); /* empty string if no args */ + if (req->args_len) { + if ((stat = sock_read(fd, r->args, req->args_len)) != APR_SUCCESS) { + return stat; + } + } + + /* read the environment variables */ + environ = apr_pcalloc(r->pool, (req->env_count + 2) *sizeof(char *)); + for (i = 0; i < req->env_count; i++) { + apr_size_t curlen; + + if ((stat = sock_read(fd, &curlen, sizeof(curlen))) != APR_SUCCESS) { + return stat; + } + environ[i] = apr_pcalloc(r->pool, curlen + 1); + if ((stat = sock_read(fd, environ[i], curlen)) != APR_SUCCESS) { + return stat; + } + } + *env = environ; + +#ifdef AP_CGID_USE_RLIMIT + if ((stat = sock_read(fd, &(req->limits), sizeof(cgid_rlimit_t))) != APR_SUCCESS) + return stat; +#endif + + return APR_SUCCESS; +} + +static apr_status_t send_req(int fd, request_rec *r, char *argv0, char **env, + int req_type) +{ + int i; + cgid_req_t req = {0}; + apr_status_t stat; + ap_unix_identity_t * ugid = ap_run_get_suexec_identity(r); + core_dir_config *core_conf = ap_get_core_module_config(r->per_dir_config); + + + if (ugid == NULL) { + req.ugid = empty_ugid; + } else { + memcpy(&req.ugid, ugid, sizeof(ap_unix_identity_t)); + } + + req.req_type = req_type; + req.ppid = parent_pid; + req.conn_id = r->connection->id; + for (req.env_count = 0; env[req.env_count]; req.env_count++) { + continue; + } + req.filename_len = strlen(r->filename); + req.argv0_len = strlen(argv0); + req.uri_len = strlen(r->uri); + req.args_len = r->args ? strlen(r->args) : 0; + req.loglevel = r->server->log.level; + + /* Write the request header */ + if (req.args_len) { + stat = sock_writev(fd, r, 5, + &req, sizeof(req), + r->filename, req.filename_len, + argv0, req.argv0_len, + r->uri, req.uri_len, + r->args, req.args_len); + } else { + stat = sock_writev(fd, r, 4, + &req, sizeof(req), + r->filename, req.filename_len, + argv0, req.argv0_len, + r->uri, req.uri_len); + } + + if (stat != APR_SUCCESS) { + return stat; + } + + /* write the environment variables */ + for (i = 0; i < req.env_count; i++) { + apr_size_t curlen = strlen(env[i]); + + if ((stat = sock_writev(fd, r, 2, &curlen, sizeof(curlen), + env[i], curlen)) != APR_SUCCESS) { + return stat; + } + } +#if defined(RLIMIT_CPU) && defined(AP_CGID_USE_RLIMIT) + if (core_conf->limit_cpu) { + req.limits.limit_cpu = *(core_conf->limit_cpu); + req.limits.limit_cpu_set = 1; + } + else { + req.limits.limit_cpu_set = 0; + } +#endif + +#if defined(AP_CGID_USE_RLIMIT) && (defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)) + if (core_conf->limit_mem) { + req.limits.limit_mem = *(core_conf->limit_mem); + req.limits.limit_mem_set = 1; + } + else { + req.limits.limit_mem_set = 0; + } + +#endif + +#if defined(RLIMIT_NPROC) && defined(AP_CGID_USE_RLIMIT) + if (core_conf->limit_nproc) { + req.limits.limit_nproc = *(core_conf->limit_nproc); + req.limits.limit_nproc_set = 1; + } + else { + req.limits.limit_nproc_set = 0; + } +#endif + +#ifdef AP_CGID_USE_RLIMIT + if ( (stat = sock_write(fd, &(req.limits), sizeof(cgid_rlimit_t))) != APR_SUCCESS) + return stat; +#endif + + return APR_SUCCESS; +} + +static void daemon_signal_handler(int sig) +{ + if (sig == SIGHUP) { + ++daemon_should_exit; + } +} + +static void cgid_child_errfn(apr_pool_t *pool, apr_status_t err, + const char *description) +{ + request_rec *r; + void *vr; + + apr_pool_userdata_get(&vr, ERRFN_USERDATA_KEY, pool); + r = vr; + + /* sure we got r, but don't call ap_log_rerror() because we don't + * have r->headers_in and possibly other storage referenced by + * ap_log_rerror() + */ + ap_log_error(APLOG_MARK, APLOG_ERR, err, r->server, APLOGNO(01241) "%s", description); +} + +static int cgid_server(void *data) +{ + int sd, sd2, rc; + mode_t omask; + apr_pool_t *ptrans; + server_rec *main_server = data; + apr_hash_t *script_hash = apr_hash_make(pcgi); + apr_status_t rv; + + apr_pool_create(&ptrans, pcgi); + + apr_signal(SIGCHLD, SIG_IGN); + apr_signal(SIGHUP, daemon_signal_handler); + + /* Close our copy of the listening sockets */ + ap_close_listeners(); + + /* cgid should use its own suexec doer */ + ap_hook_get_suexec_identity(cgid_suexec_id_doer, NULL, NULL, + APR_HOOK_REALLY_FIRST); + apr_hook_sort_all(); + + if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, APLOGNO(01242) + "Couldn't create unix domain socket"); + return errno; + } + + omask = umask(0077); /* so that only Apache can use socket */ + rc = bind(sd, (struct sockaddr *)server_addr, server_addr_len); + umask(omask); /* can't fail, so can't clobber errno */ + if (rc < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, APLOGNO(01243) + "Couldn't bind unix domain socket %s", + sockname); + return errno; + } + + /* Not all flavors of unix use the current umask for AF_UNIX perms */ + rv = apr_file_perms_set(sockname, APR_FPROT_UREAD|APR_FPROT_UWRITE|APR_FPROT_UEXECUTE); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, main_server, APLOGNO(01244) + "Couldn't set permissions on unix domain socket %s", + sockname); + return rv; + } + + if (listen(sd, DEFAULT_CGID_LISTENBACKLOG) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, APLOGNO(01245) + "Couldn't listen on unix domain socket"); + return errno; + } + + if (!geteuid()) { + if (chown(sockname, ap_unixd_config.user_id, -1) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, APLOGNO(01246) + "Couldn't change owner of unix domain socket %s", + sockname); + return errno; + } + } + + apr_pool_cleanup_register(pcgi, (void *)((long)sd), + close_unix_socket, close_unix_socket); + + /* if running as root, switch to configured user/group */ + if ((rc = ap_run_drop_privileges(pcgi, ap_server_conf)) != 0) { + return rc; + } + + while (!daemon_should_exit) { + int errfileno = STDERR_FILENO; + char *argv0 = NULL; + char **env = NULL; + const char * const *argv; + apr_int32_t in_pipe; + apr_int32_t out_pipe; + apr_int32_t err_pipe; + apr_cmdtype_e cmd_type; + request_rec *r; + apr_procattr_t *procattr = NULL; + apr_proc_t *procnew = NULL; + apr_file_t *inout; + cgid_req_t cgid_req; + apr_status_t stat; + void *key; + apr_socklen_t len; + struct sockaddr_un unix_addr; + + apr_pool_clear(ptrans); + + len = sizeof(unix_addr); + sd2 = accept(sd, (struct sockaddr *)&unix_addr, &len); + if (sd2 < 0) { +#if defined(ENETDOWN) + if (errno == ENETDOWN) { + /* The network has been shut down, no need to continue. Die gracefully */ + ++daemon_should_exit; + } +#endif + if (errno != EINTR) { + ap_log_error(APLOG_MARK, APLOG_ERR, errno, + (server_rec *)data, APLOGNO(01247) + "Error accepting on cgid socket"); + } + continue; + } + + r = apr_pcalloc(ptrans, sizeof(request_rec)); + procnew = apr_pcalloc(ptrans, sizeof(*procnew)); + r->pool = ptrans; + stat = get_req(sd2, r, &argv0, &env, &cgid_req); + if (stat != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, stat, + main_server, APLOGNO(01248) + "Error reading request on cgid socket"); + close(sd2); + continue; + } + + if (cgid_req.ppid != parent_pid) { + ap_log_error(APLOG_MARK, APLOG_CRIT, 0, main_server, APLOGNO(01249) + "CGI request received from wrong server instance; " + "see ScriptSock directive"); + close(sd2); + continue; + } + + if (cgid_req.req_type == GETPID_REQ) { + pid_t pid; + apr_status_t rv; + + pid = (pid_t)((long)apr_hash_get(script_hash, &cgid_req.conn_id, sizeof(cgid_req.conn_id))); + rv = sock_write(sd2, &pid, sizeof(pid)); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, + main_server, APLOGNO(01250) + "Error writing pid %" APR_PID_T_FMT " to handler", pid); + } + close(sd2); + continue; + } + + apr_os_file_put(&r->server->error_log, &errfileno, 0, r->pool); + apr_os_file_put(&inout, &sd2, 0, r->pool); + + if (cgid_req.req_type == SSI_REQ) { + in_pipe = APR_NO_PIPE; + out_pipe = APR_FULL_BLOCK; + err_pipe = APR_NO_PIPE; + cmd_type = APR_SHELLCMD; + } + else { + in_pipe = APR_CHILD_BLOCK; + out_pipe = APR_CHILD_BLOCK; + err_pipe = APR_CHILD_BLOCK; + cmd_type = APR_PROGRAM; + } + + if (((rc = apr_procattr_create(&procattr, ptrans)) != APR_SUCCESS) || + ((cgid_req.req_type == CGI_REQ) && + (((rc = apr_procattr_io_set(procattr, + in_pipe, + out_pipe, + err_pipe)) != APR_SUCCESS) || + /* XXX apr_procattr_child_*_set() is creating an unnecessary + * pipe between this process and the child being created... + * It is cleaned up with the temporary pool for this request. + */ + ((rc = apr_procattr_child_err_set(procattr, r->server->error_log, NULL)) != APR_SUCCESS) || + ((rc = apr_procattr_child_in_set(procattr, inout, NULL)) != APR_SUCCESS))) || + ((rc = apr_procattr_child_out_set(procattr, inout, NULL)) != APR_SUCCESS) || + ((rc = apr_procattr_dir_set(procattr, + ap_make_dirstr_parent(r->pool, r->filename))) != APR_SUCCESS) || + ((rc = apr_procattr_cmdtype_set(procattr, cmd_type)) != APR_SUCCESS) || +#ifdef AP_CGID_USE_RLIMIT +#ifdef RLIMIT_CPU + ( (cgid_req.limits.limit_cpu_set) && ((rc = apr_procattr_limit_set(procattr, APR_LIMIT_CPU, + &cgid_req.limits.limit_cpu)) != APR_SUCCESS)) || +#endif +#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) + ( (cgid_req.limits.limit_mem_set) && ((rc = apr_procattr_limit_set(procattr, APR_LIMIT_MEM, + &cgid_req.limits.limit_mem)) != APR_SUCCESS)) || +#endif +#ifdef RLIMIT_NPROC + ( (cgid_req.limits.limit_nproc_set) && ((rc = apr_procattr_limit_set(procattr, APR_LIMIT_NPROC, + &cgid_req.limits.limit_nproc)) != APR_SUCCESS)) || +#endif +#endif + + ((rc = apr_procattr_child_errfn_set(procattr, cgid_child_errfn)) != APR_SUCCESS)) { + /* Something bad happened, tell the world. + * ap_log_rerror() won't work because the header table used by + * ap_log_rerror() hasn't been replicated in the phony r + */ + ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server, APLOGNO(01251) + "couldn't set child process attributes: %s", r->filename); + + procnew->pid = 0; /* no process to clean up */ + close(sd2); + } + else { + apr_pool_userdata_set(r, ERRFN_USERDATA_KEY, apr_pool_cleanup_null, ptrans); + + argv = (const char * const *)create_argv(r->pool, NULL, NULL, NULL, argv0, r->args); + + /* We want to close sd2 for the new CGI process too. + * If it is left open it'll make ap_pass_brigade() block + * waiting for EOF if CGI forked something running long. + * close(sd2) here should be okay, as CGI channel + * is already dup()ed by apr_procattr_child_{in,out}_set() + * above. + */ + close(sd2); + + if (memcmp(&empty_ugid, &cgid_req.ugid, sizeof(empty_ugid))) { + /* We have a valid identity, and can be sure that + * cgid_suexec_id_doer will return a valid ugid + */ + rc = ap_os_create_privileged_process(r, procnew, argv0, argv, + (const char * const *)env, + procattr, ptrans); + } else { + rc = apr_proc_create(procnew, argv0, argv, + (const char * const *)env, + procattr, ptrans); + } + + if (rc != APR_SUCCESS) { + /* Bad things happened. Everyone should have cleaned up. + * ap_log_rerror() won't work because the header table used by + * ap_log_rerror() hasn't been replicated in the phony r + */ + ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server, APLOGNO(01252) + "couldn't create child process: %d: %s", rc, + apr_filepath_name_get(r->filename)); + + procnew->pid = 0; /* no process to clean up */ + } + } + + /* If the script process was created, remember the pid for + * later cleanup. If the script process wasn't created, clear + * out any prior pid with the same key. + * + * We don't want to leak storage for the key, so only allocate + * a key if the key doesn't exist yet in the hash; there are + * only a limited number of possible keys (one for each + * possible thread in the server), so we can allocate a copy + * of the key the first time a thread has a cgid request. + * Note that apr_hash_set() only uses the storage passed in + * for the key if it is adding the key to the hash for the + * first time; new key storage isn't needed for replacing the + * existing value of a key. + */ + + if (apr_hash_get(script_hash, &cgid_req.conn_id, sizeof(cgid_req.conn_id))) { + key = &cgid_req.conn_id; + } + else { + key = apr_pmemdup(pcgi, &cgid_req.conn_id, sizeof(cgid_req.conn_id)); + } + apr_hash_set(script_hash, key, sizeof(cgid_req.conn_id), + (void *)((long)procnew->pid)); + } + return -1; /* should be <= 0 to distinguish from startup errors */ +} + +static int cgid_start(apr_pool_t *p, server_rec *main_server, + apr_proc_t *procnew) +{ + + daemon_should_exit = 0; /* clear setting from previous generation */ + if ((daemon_pid = fork()) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, APLOGNO(01253) + "mod_cgid: Couldn't spawn cgid daemon process"); + return DECLINED; + } + else if (daemon_pid == 0) { + if (pcgi == NULL) { + apr_pool_create(&pcgi, p); + } + exit(cgid_server(main_server) > 0 ? DAEMON_STARTUP_ERROR : -1); + } + procnew->pid = daemon_pid; + procnew->err = procnew->in = procnew->out = NULL; + apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT); +#if APR_HAS_OTHER_CHILD + apr_proc_other_child_register(procnew, cgid_maint, procnew, NULL, p); +#endif + return OK; +} + +static int cgid_pre_config(apr_pool_t *pconf, apr_pool_t *plog, + apr_pool_t *ptemp) +{ + sockname = ap_append_pid(pconf, DEFAULT_SOCKET, "."); + return OK; +} + +static int cgid_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, + server_rec *main_server) +{ + apr_proc_t *procnew = NULL; + const char *userdata_key = "cgid_init"; + int ret = OK; + void *data; + + root_server = main_server; + root_pool = p; + + apr_pool_userdata_get(&data, userdata_key, main_server->process->pool); + if (!data) { + procnew = apr_pcalloc(main_server->process->pool, sizeof(*procnew)); + procnew->pid = -1; + procnew->err = procnew->in = procnew->out = NULL; + apr_pool_userdata_set((const void *)procnew, userdata_key, + apr_pool_cleanup_null, main_server->process->pool); + return ret; + } + else { + procnew = data; + } + + if (ap_state_query(AP_SQ_MAIN_STATE) != AP_SQ_MS_CREATE_PRE_CONFIG) { + char *tmp_sockname; + + parent_pid = getpid(); + tmp_sockname = ap_runtime_dir_relative(p, sockname); + if (strlen(tmp_sockname) > sizeof(server_addr->sun_path) - 1) { + tmp_sockname[sizeof(server_addr->sun_path)] = '\0'; + ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server, APLOGNO(01254) + "The length of the ScriptSock path exceeds maximum, " + "truncating to %s", tmp_sockname); + } + sockname = tmp_sockname; + + server_addr_len = APR_OFFSETOF(struct sockaddr_un, sun_path) + strlen(sockname); + server_addr = (struct sockaddr_un *)apr_palloc(p, server_addr_len + 1); + server_addr->sun_family = AF_UNIX; + strcpy(server_addr->sun_path, sockname); + + ret = cgid_start(p, main_server, procnew); + if (ret != OK ) { + return ret; + } + cgid_pfn_reg_with_ssi = APR_RETRIEVE_OPTIONAL_FN(ap_register_include_handler); + cgid_pfn_gtv = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_get_tag_and_value); + cgid_pfn_ps = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_parse_string); + + if ((cgid_pfn_reg_with_ssi) && (cgid_pfn_gtv) && (cgid_pfn_ps)) { + /* Required by mod_include filter. This is how mod_cgid registers + * with mod_include to provide processing of the exec directive. + */ + cgid_pfn_reg_with_ssi("exec", handle_exec); + } + } + return ret; +} + +static void *create_cgid_config(apr_pool_t *p, server_rec *s) +{ + cgid_server_conf *c = + (cgid_server_conf *) apr_pcalloc(p, sizeof(cgid_server_conf)); + + c->logname = NULL; + c->logbytes = DEFAULT_LOGBYTES; + c->bufbytes = DEFAULT_BUFBYTES; + return c; +} + +static void *merge_cgid_config(apr_pool_t *p, void *basev, void *overridesv) +{ + cgid_server_conf *base = (cgid_server_conf *) basev, *overrides = (cgid_server_conf *) overridesv; + + return overrides->logname ? overrides : base; +} + +static void *create_cgid_dirconf(apr_pool_t *p, char *dummy) +{ + cgid_dirconf *c = (cgid_dirconf *) apr_pcalloc(p, sizeof(cgid_dirconf)); + return c; +} + +static const char *set_scriptlog(cmd_parms *cmd, void *dummy, const char *arg) + +{ + server_rec *s = cmd->server; + cgid_server_conf *conf = ap_get_module_config(s->module_config, + &cgid_module); + + conf->logname = ap_server_root_relative(cmd->pool, arg); + + if (!conf->logname) { + return apr_pstrcat(cmd->pool, "Invalid ScriptLog path ", + arg, NULL); + } + return NULL; +} + +static const char *set_scriptlog_length(cmd_parms *cmd, void *dummy, const char *arg) +{ + server_rec *s = cmd->server; + cgid_server_conf *conf = ap_get_module_config(s->module_config, + &cgid_module); + + conf->logbytes = atol(arg); + return NULL; +} + +static const char *set_scriptlog_buffer(cmd_parms *cmd, void *dummy, const char *arg) +{ + server_rec *s = cmd->server; + cgid_server_conf *conf = ap_get_module_config(s->module_config, + &cgid_module); + + conf->bufbytes = atoi(arg); + return NULL; +} + +static const char *set_script_socket(cmd_parms *cmd, void *dummy, const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + /* Make sure the pid is appended to the sockname */ + sockname = ap_append_pid(cmd->pool, arg, "."); + sockname = ap_runtime_dir_relative(cmd->pool, sockname); + + if (!sockname) { + return apr_pstrcat(cmd->pool, "Invalid ScriptSock path", + arg, NULL); + } + + return NULL; +} +static const char *set_script_timeout(cmd_parms *cmd, void *dummy, const char *arg) +{ + cgid_dirconf *dc = dummy; + + if (ap_timeout_parameter_parse(arg, &dc->timeout, "s") != APR_SUCCESS) { + return "CGIDScriptTimeout has wrong format"; + } + + return NULL; +} +static const command_rec cgid_cmds[] = +{ + AP_INIT_TAKE1("ScriptLog", set_scriptlog, NULL, RSRC_CONF, + "the name of a log for script debugging info"), + AP_INIT_TAKE1("ScriptLogLength", set_scriptlog_length, NULL, RSRC_CONF, + "the maximum length (in bytes) of the script debug log"), + AP_INIT_TAKE1("ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF, + "the maximum size (in bytes) to record of a POST request"), + AP_INIT_TAKE1("ScriptSock", set_script_socket, NULL, RSRC_CONF, + "the name of the socket to use for communication with " + "the cgi daemon."), + AP_INIT_TAKE1("CGIDScriptTimeout", set_script_timeout, NULL, RSRC_CONF | ACCESS_CONF, + "The amount of time to wait between successful reads from " + "the CGI script, in seconds."), + + {NULL} +}; + +static int log_scripterror(request_rec *r, cgid_server_conf * conf, int ret, + apr_status_t rv, char *error) +{ + apr_file_t *f = NULL; + struct stat finfo; + char time_str[APR_CTIME_LEN]; + int log_flags = rv ? APLOG_ERR : APLOG_ERR; + + /* Intentional no APLOGNO */ + /* Callee provides APLOGNO in error text */ + ap_log_rerror(APLOG_MARK, log_flags, rv, r, + "%s: %s", error, r->filename); + + /* XXX Very expensive mainline case! Open, then getfileinfo! */ + if (!conf->logname || + ((stat(conf->logname, &finfo) == 0) + && (finfo.st_size > conf->logbytes)) || + (apr_file_open(&f, conf->logname, + APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT, r->pool) != APR_SUCCESS)) { + return ret; + } + + /* "%% [Wed Jun 19 10:53:21 1996] GET /cgid-bin/printenv HTTP/1.0" */ + apr_ctime(time_str, apr_time_now()); + apr_file_printf(f, "%%%% [%s] %s %s%s%s %s\n", time_str, r->method, r->uri, + r->args ? "?" : "", r->args ? r->args : "", r->protocol); + /* "%% 500 /usr/local/apache/cgid-bin */ + apr_file_printf(f, "%%%% %d %s\n", ret, r->filename); + + apr_file_printf(f, "%%error\n%s\n", error); + + apr_file_close(f); + return ret; +} + +static int log_script(request_rec *r, cgid_server_conf * conf, int ret, + char *dbuf, const char *sbuf, apr_bucket_brigade *bb, + apr_file_t *script_err) +{ + const apr_array_header_t *hdrs_arr = apr_table_elts(r->headers_in); + const apr_table_entry_t *hdrs = (apr_table_entry_t *) hdrs_arr->elts; + char argsbuffer[HUGE_STRING_LEN]; + apr_file_t *f = NULL; + apr_bucket *e; + const char *buf; + apr_size_t len; + apr_status_t rv; + int first; + int i; + struct stat finfo; + char time_str[APR_CTIME_LEN]; + + /* XXX Very expensive mainline case! Open, then getfileinfo! */ + if (!conf->logname || + ((stat(conf->logname, &finfo) == 0) + && (finfo.st_size > conf->logbytes)) || + (apr_file_open(&f, conf->logname, + APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT, r->pool) != APR_SUCCESS)) { + /* Soak up script output */ + discard_script_output(bb); + if (script_err) { + while (apr_file_gets(argsbuffer, HUGE_STRING_LEN, + script_err) == APR_SUCCESS) + continue; + } + return ret; + } + + /* "%% [Wed Jun 19 10:53:21 1996] GET /cgid-bin/printenv HTTP/1.0" */ + apr_ctime(time_str, apr_time_now()); + apr_file_printf(f, "%%%% [%s] %s %s%s%s %s\n", time_str, r->method, r->uri, + r->args ? "?" : "", r->args ? r->args : "", r->protocol); + /* "%% 500 /usr/local/apache/cgid-bin" */ + apr_file_printf(f, "%%%% %d %s\n", ret, r->filename); + + apr_file_puts("%request\n", f); + for (i = 0; i < hdrs_arr->nelts; ++i) { + if (!hdrs[i].key) + continue; + apr_file_printf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val); + } + if ((r->method_number == M_POST || r->method_number == M_PUT) + && *dbuf) { + apr_file_printf(f, "\n%s\n", dbuf); + } + + apr_file_puts("%response\n", f); + hdrs_arr = apr_table_elts(r->err_headers_out); + hdrs = (const apr_table_entry_t *) hdrs_arr->elts; + + for (i = 0; i < hdrs_arr->nelts; ++i) { + if (!hdrs[i].key) + continue; + apr_file_printf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val); + } + + if (sbuf && *sbuf) + apr_file_printf(f, "%s\n", sbuf); + + first = 1; + + for (e = APR_BRIGADE_FIRST(bb); + e != APR_BRIGADE_SENTINEL(bb); + e = APR_BUCKET_NEXT(e)) + { + if (APR_BUCKET_IS_EOS(e)) { + break; + } + rv = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ); + if (rv != APR_SUCCESS || (len == 0)) { + break; + } + if (first) { + apr_file_puts("%stdout\n", f); + first = 0; + } + apr_file_write_full(f, buf, len, NULL); + apr_file_puts("\n", f); + } + + if (script_err) { + if (apr_file_gets(argsbuffer, HUGE_STRING_LEN, + script_err) == APR_SUCCESS) { + apr_file_puts("%stderr\n", f); + apr_file_puts(argsbuffer, f); + while (apr_file_gets(argsbuffer, HUGE_STRING_LEN, + script_err) == APR_SUCCESS) + apr_file_puts(argsbuffer, f); + apr_file_puts("\n", f); + } + } + + if (script_err) { + apr_file_close(script_err); + } + + apr_file_close(f); + return ret; +} + +static int connect_to_daemon(int *sdptr, request_rec *r, + cgid_server_conf *conf) +{ + int sd; + int connect_tries; + int connect_errno; + apr_interval_time_t sliding_timer; + + connect_tries = 0; + sliding_timer = 100000; /* 100 milliseconds */ + while (1) { + connect_errno = 0; + ++connect_tries; + if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + return log_scripterror(r, conf, HTTP_INTERNAL_SERVER_ERROR, errno, + APLOGNO(01255) "unable to create socket to cgi daemon"); + } + if (connect(sd, (struct sockaddr *)server_addr, server_addr_len) < 0) { + /* Save errno for later */ + connect_errno = errno; + /* ECONNREFUSED means the listen queue is full; ENOENT means that + * the cgid server either hasn't started up yet, or we're pointing + * at the wrong socket file */ + if ((errno == ECONNREFUSED || errno == ENOENT) && + connect_tries < DEFAULT_CONNECT_ATTEMPTS) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, errno, r, APLOGNO(01256) + "connect #%d to cgi daemon failed, sleeping before retry", + connect_tries); + close(sd); + apr_sleep(sliding_timer); + if (sliding_timer < apr_time_from_sec(2)) { + sliding_timer *= 2; + } + } + else { + close(sd); + return log_scripterror(r, conf, HTTP_SERVICE_UNAVAILABLE, errno, APLOGNO(01257) + "unable to connect to cgi daemon after multiple tries"); + } + } + else { + apr_pool_cleanup_register(r->pool, (void *)((long)sd), + close_unix_socket, apr_pool_cleanup_null); + break; /* we got connected! */ + } + + /* If we didn't find the socket but the server was not recently restarted, + * chances are there's something wrong with the cgid daemon + */ + if (connect_errno == ENOENT && + apr_time_sec(apr_time_now() - ap_scoreboard_image->global->restart_time) > + DEFAULT_CONNECT_STARTUP_DELAY) { + return log_scripterror(r, conf, HTTP_SERVICE_UNAVAILABLE, connect_errno, + apr_pstrcat(r->pool, APLOGNO(02833) "ScriptSock ", sockname, " does not exist", NULL)); + } + + /* gotta try again, but make sure the cgid daemon is still around */ + if (connect_errno != ENOENT && kill(daemon_pid, 0) != 0) { + return log_scripterror(r, conf, HTTP_SERVICE_UNAVAILABLE, connect_errno, APLOGNO(01258) + "cgid daemon is gone; is Apache terminating?"); + } + } + *sdptr = sd; + return OK; +} + +static void discard_script_output(apr_bucket_brigade *bb) +{ + apr_bucket *e; + const char *buf; + apr_size_t len; + apr_status_t rv; + + for (e = APR_BRIGADE_FIRST(bb); + e != APR_BRIGADE_SENTINEL(bb); + e = APR_BUCKET_NEXT(e)) + { + if (APR_BUCKET_IS_EOS(e)) { + break; + } + rv = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ); + if (rv != APR_SUCCESS) { + break; + } + } +} + +/**************************************************************** + * + * Actual cgid handling... + */ + +struct cleanup_script_info { + request_rec *r; + cgid_server_conf *conf; + pid_t pid; +}; + +static apr_status_t dead_yet(pid_t pid, apr_interval_time_t max_wait) +{ + apr_interval_time_t interval = 10000; /* 10 ms */ + apr_interval_time_t total = 0; + + do { +#ifdef _AIX + /* On AIX, for processes like mod_cgid's script children where + * SIGCHLD is ignored, kill(pid,0) returns success for up to + * one second after the script child exits, based on when a + * daemon runs to clean up unnecessary process table entries. + * getpgid() can report the proper info (-1/ESRCH) immediately. + */ + if (getpgid(pid) < 0) { +#else + if (kill(pid, 0) < 0) { +#endif + return APR_SUCCESS; + } + apr_sleep(interval); + total = total + interval; + if (interval < 500000) { + interval *= 2; + } + } while (total < max_wait); + return APR_EGENERAL; +} + +static apr_status_t cleanup_nonchild_process(request_rec *r, pid_t pid) +{ + kill(pid, SIGTERM); /* in case it isn't dead yet */ + if (dead_yet(pid, apr_time_from_sec(3)) == APR_SUCCESS) { + return APR_SUCCESS; + } + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01259) + "CGI process %" APR_PID_T_FMT " didn't exit, sending SIGKILL", + pid); + kill(pid, SIGKILL); + if (dead_yet(pid, apr_time_from_sec(3)) == APR_SUCCESS) { + return APR_SUCCESS; + } + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01260) + "CGI process %" APR_PID_T_FMT " didn't exit, sending SIGKILL again", + pid); + kill(pid, SIGKILL); + + return APR_EGENERAL; +} + +static apr_status_t get_cgi_pid(request_rec *r, cgid_server_conf *conf, pid_t *pid) { + cgid_req_t req = {0}; + apr_status_t stat; + int rc, sd; + + rc = connect_to_daemon(&sd, r, conf); + if (rc != OK) { + return APR_EGENERAL; + } + + req.req_type = GETPID_REQ; + req.ppid = parent_pid; + req.conn_id = r->connection->id; + + stat = sock_write(sd, &req, sizeof(req)); + if (stat != APR_SUCCESS) { + return stat; + } + + /* wait for pid of script */ + stat = sock_read(sd, pid, sizeof(*pid)); + if (stat != APR_SUCCESS) { + return stat; + } + + if (pid == 0) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01261) + "daemon couldn't find CGI process for connection %lu", + r->connection->id); + return APR_EGENERAL; + } + + return APR_SUCCESS; +} + + +static apr_status_t cleanup_script(void *vptr) +{ + struct cleanup_script_info *info = vptr; + return cleanup_nonchild_process(info->r, info->pid); +} + +static int cgid_handler(request_rec *r) +{ + int retval, nph, dbpos; + char *argv0, *dbuf; + apr_bucket_brigade *bb; + apr_bucket *b; + cgid_server_conf *conf; + int is_included; + int seen_eos, child_stopped_reading; + int sd; + char **env; + apr_file_t *tempsock; + struct cleanup_script_info *info; + apr_status_t rv; + cgid_dirconf *dc; + + if (strcmp(r->handler, CGI_MAGIC_TYPE) && strcmp(r->handler, "cgi-script")) { + return DECLINED; + } + + conf = ap_get_module_config(r->server->module_config, &cgid_module); + dc = ap_get_module_config(r->per_dir_config, &cgid_module); + + + is_included = !strcmp(r->protocol, "INCLUDED"); + + if ((argv0 = strrchr(r->filename, '/')) != NULL) { + argv0++; + } + else { + argv0 = r->filename; + } + + nph = !(strncmp(argv0, "nph-", 4)); + + argv0 = r->filename; + + if (!(ap_allow_options(r) & OPT_EXECCGI) && !is_scriptaliased(r)) { + return log_scripterror(r, conf, HTTP_FORBIDDEN, 0, APLOGNO(01262) + "Options ExecCGI is off in this directory"); + } + + if (nph && is_included) { + return log_scripterror(r, conf, HTTP_FORBIDDEN, 0, APLOGNO(01263) + "attempt to include NPH CGI script"); + } + +#if defined(OS2) || defined(WIN32) +#error mod_cgid does not work on this platform. If you teach it to, look +#error at mod_cgi.c for required code in this path. +#else + if (r->finfo.filetype == APR_NOFILE) { + return log_scripterror(r, conf, HTTP_NOT_FOUND, 0, APLOGNO(01264) + "script not found or unable to stat"); + } +#endif + if (r->finfo.filetype == APR_DIR) { + return log_scripterror(r, conf, HTTP_FORBIDDEN, 0, APLOGNO(01265) + "attempt to invoke directory as script"); + } + + if ((r->used_path_info == AP_REQ_REJECT_PATH_INFO) && + r->path_info && *r->path_info) + { + /* default to accept */ + return log_scripterror(r, conf, HTTP_NOT_FOUND, 0, APLOGNO(01266) + "AcceptPathInfo off disallows user's path"); + } + /* + if (!ap_suexec_enabled) { + if (!ap_can_exec(&r->finfo)) + return log_scripterror(r, conf, HTTP_FORBIDDEN, 0, APLOGNO(01267) + "file permissions deny server execution"); + } + */ + + /* + * httpd core function used to add common environment variables like + * DOCUMENT_ROOT. + */ + ap_add_common_vars(r); + ap_add_cgi_vars(r); + env = ap_create_environment(r->pool, r->subprocess_env); + + if ((retval = connect_to_daemon(&sd, r, conf)) != OK) { + return retval; + } + + rv = send_req(sd, r, argv0, env, CGI_REQ); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01268) + "write to cgi daemon process"); + } + + info = apr_palloc(r->pool, sizeof(struct cleanup_script_info)); + info->conf = conf; + info->r = r; + rv = get_cgi_pid(r, conf, &(info->pid)); + + if (APR_SUCCESS == rv){ + apr_pool_cleanup_register(r->pool, info, + cleanup_script, + apr_pool_cleanup_null); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, "error determining cgi PID"); + } + + /* We are putting the socket discriptor into an apr_file_t so that we can + * use a pipe bucket to send the data to the client. APR will create + * a cleanup for the apr_file_t which will close the socket, so we'll + * get rid of the cleanup we registered when we created the socket. + */ + + apr_os_pipe_put_ex(&tempsock, &sd, 1, r->pool); + if (dc->timeout > 0) { + apr_file_pipe_timeout_set(tempsock, dc->timeout); + } + else { + apr_file_pipe_timeout_set(tempsock, r->server->timeout); + } + apr_pool_cleanup_kill(r->pool, (void *)((long)sd), close_unix_socket); + + /* Transfer any put/post args, CERN style... + * Note that we already ignore SIGPIPE in the core server. + */ + bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); + seen_eos = 0; + child_stopped_reading = 0; + dbuf = NULL; + dbpos = 0; + if (conf->logname) { + dbuf = apr_palloc(r->pool, conf->bufbytes + 1); + } + do { + apr_bucket *bucket; + + rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, + APR_BLOCK_READ, HUGE_STRING_LEN); + + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01270) + "Error reading request entity data"); + return ap_map_http_request_error(rv, HTTP_BAD_REQUEST); + } + + for (bucket = APR_BRIGADE_FIRST(bb); + bucket != APR_BRIGADE_SENTINEL(bb); + bucket = APR_BUCKET_NEXT(bucket)) + { + const char *data; + apr_size_t len; + + if (APR_BUCKET_IS_EOS(bucket)) { + seen_eos = 1; + break; + } + + /* We can't do much with this. */ + if (APR_BUCKET_IS_FLUSH(bucket)) { + continue; + } + + /* If the child stopped, we still must read to EOS. */ + if (child_stopped_reading) { + continue; + } + + /* read */ + apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ); + + if (conf->logname && dbpos < conf->bufbytes) { + int cursize; + + if ((dbpos + len) > conf->bufbytes) { + cursize = conf->bufbytes - dbpos; + } + else { + cursize = len; + } + memcpy(dbuf + dbpos, data, cursize); + dbpos += cursize; + } + + /* Keep writing data to the child until done or too much time + * elapses with no progress or an error occurs. + */ + rv = apr_file_write_full(tempsock, data, len, NULL); + + if (rv != APR_SUCCESS) { + /* silly script stopped reading, soak up remaining message */ + child_stopped_reading = 1; + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02651) + "Error writing request body to script %s", + r->filename); + + } + } + apr_brigade_cleanup(bb); + } + while (!seen_eos); + + if (conf->logname) { + dbuf[dbpos] = '\0'; + } + + /* we're done writing, or maybe we didn't write at all; + * force EOF on child's stdin so that the cgi detects end (or + * absence) of data + */ + shutdown(sd, 1); + + /* Handle script return... */ + if (!nph) { + conn_rec *c = r->connection; + const char *location; + char sbuf[MAX_STRING_LEN]; + int ret; + + bb = apr_brigade_create(r->pool, c->bucket_alloc); + b = apr_bucket_pipe_create(tempsock, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, b); + b = apr_bucket_eos_create(c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, b); + + if ((ret = ap_scan_script_header_err_brigade_ex(r, bb, sbuf, + APLOG_MODULE_INDEX))) + { + ret = log_script(r, conf, ret, dbuf, sbuf, bb, NULL); + + /* + * ret could be HTTP_NOT_MODIFIED in the case that the CGI script + * does not set an explicit status and ap_meets_conditions, which + * is called by ap_scan_script_header_err_brigade, detects that + * the conditions of the requests are met and the response is + * not modified. + * In this case set r->status and return OK in order to prevent + * running through the error processing stack as this would + * break with mod_cache, if the conditions had been set by + * mod_cache itself to validate a stale entity. + * BTW: We circumvent the error processing stack anyway if the + * CGI script set an explicit status code (whatever it is) and + * the only possible values for ret here are: + * + * HTTP_NOT_MODIFIED (set by ap_meets_conditions) + * HTTP_PRECONDITION_FAILED (set by ap_meets_conditions) + * HTTP_INTERNAL_SERVER_ERROR (if something went wrong during the + * processing of the response of the CGI script, e.g broken headers + * or a crashed CGI process). + */ + if (ret == HTTP_NOT_MODIFIED) { + r->status = ret; + return OK; + } + + return ret; + } + + location = apr_table_get(r->headers_out, "Location"); + + if (location && location[0] == '/' && r->status == 200) { + + /* Soak up all the script output */ + discard_script_output(bb); + apr_brigade_destroy(bb); + /* This redirect needs to be a GET no matter what the original + * method was. + */ + r->method = "GET"; + r->method_number = M_GET; + + /* We already read the message body (if any), so don't allow + * the redirected request to think it has one. We can ignore + * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR. + */ + apr_table_unset(r->headers_in, "Content-Length"); + + ap_internal_redirect_handler(location, r); + return OK; + } + else if (location && r->status == 200) { + /* XXX: Note that if a script wants to produce its own Redirect + * body, it now has to explicitly *say* "Status: 302" + */ + discard_script_output(bb); + apr_brigade_destroy(bb); + return HTTP_MOVED_TEMPORARILY; + } + + rv = ap_pass_brigade(r->output_filters, bb); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, + "Failed to flush CGI output to client"); + } + } + + if (nph) { + conn_rec *c = r->connection; + struct ap_filter_t *cur; + + /* get rid of all filters up through protocol... since we + * haven't parsed off the headers, there is no way they can + * work + */ + + cur = r->proto_output_filters; + while (cur && cur->frec->ftype < AP_FTYPE_CONNECTION) { + cur = cur->next; + } + r->output_filters = r->proto_output_filters = cur; + + bb = apr_brigade_create(r->pool, c->bucket_alloc); + b = apr_bucket_pipe_create(tempsock, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, b); + b = apr_bucket_eos_create(c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, b); + ap_pass_brigade(r->output_filters, bb); + } + + return OK; /* NOT r->status, even if it has changed. */ +} + + + + +/*============================================================================ + *============================================================================ + * This is the beginning of the cgi filter code moved from mod_include. This + * is the code required to handle the "exec" SSI directive. + *============================================================================ + *============================================================================*/ +static apr_status_t include_cgi(include_ctx_t *ctx, ap_filter_t *f, + apr_bucket_brigade *bb, char *s) +{ + request_rec *r = f->r; + request_rec *rr = ap_sub_req_lookup_uri(s, r, f->next); + int rr_status; + + if (rr->status != HTTP_OK) { + ap_destroy_sub_req(rr); + return APR_EGENERAL; + } + + /* No hardwired path info or query allowed */ + if ((rr->path_info && rr->path_info[0]) || rr->args) { + ap_destroy_sub_req(rr); + return APR_EGENERAL; + } + if (rr->finfo.filetype != APR_REG) { + ap_destroy_sub_req(rr); + return APR_EGENERAL; + } + + /* Script gets parameters of the *document*, for back compatibility */ + rr->path_info = r->path_info; /* hard to get right; see mod_cgi.c */ + rr->args = r->args; + + /* Force sub_req to be treated as a CGI request, even if ordinary + * typing rules would have called it something else. + */ + ap_set_content_type(rr, CGI_MAGIC_TYPE); + + /* Run it. */ + rr_status = ap_run_sub_req(rr); + if (ap_is_HTTP_REDIRECT(rr_status)) { + const char *location = apr_table_get(rr->headers_out, "Location"); + + if (location) { + char *buffer; + + location = ap_escape_html(rr->pool, location); + buffer = apr_pstrcat(ctx->pool, "<a href=\"", location, "\">", + location, "</a>", NULL); + + APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(buffer, + strlen(buffer), ctx->pool, + f->c->bucket_alloc)); + } + } + + ap_destroy_sub_req(rr); + + return APR_SUCCESS; +} + +/* This is the special environment used for running the "exec cmd=" + * variety of SSI directives. + */ +static void add_ssi_vars(request_rec *r) +{ + apr_table_t *e = r->subprocess_env; + + if (r->path_info && r->path_info[0] != '\0') { + request_rec *pa_req; + + apr_table_setn(e, "PATH_INFO", ap_escape_shell_cmd(r->pool, r->path_info)); + + pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r, NULL); + if (pa_req->filename) { + apr_table_setn(e, "PATH_TRANSLATED", + apr_pstrcat(r->pool, pa_req->filename, pa_req->path_info, NULL)); + } + ap_destroy_sub_req(pa_req); + } + + if (r->args) { + char *arg_copy = apr_pstrdup(r->pool, r->args); + + apr_table_setn(e, "QUERY_STRING", r->args); + ap_unescape_url(arg_copy); + apr_table_setn(e, "QUERY_STRING_UNESCAPED", ap_escape_shell_cmd(r->pool, arg_copy)); + } +} + +static int include_cmd(include_ctx_t *ctx, ap_filter_t *f, + apr_bucket_brigade *bb, char *command) +{ + char **env; + int sd; + int retval; + apr_file_t *tempsock = NULL; + request_rec *r = f->r; + cgid_server_conf *conf = ap_get_module_config(r->server->module_config, + &cgid_module); + cgid_dirconf *dc = ap_get_module_config(r->per_dir_config, &cgid_module); + + struct cleanup_script_info *info; + apr_status_t rv; + + add_ssi_vars(r); + env = ap_create_environment(r->pool, r->subprocess_env); + + if ((retval = connect_to_daemon(&sd, r, conf)) != OK) { + return retval; + } + + send_req(sd, r, command, env, SSI_REQ); + + info = apr_palloc(r->pool, sizeof(struct cleanup_script_info)); + info->conf = conf; + info->r = r; + rv = get_cgi_pid(r, conf, &(info->pid)); + if (APR_SUCCESS == rv) { + /* for this type of request, the script is invoked through an + * intermediate shell process... cleanup_script is only able + * to knock out the shell process, not the actual script + */ + apr_pool_cleanup_register(r->pool, info, + cleanup_script, + apr_pool_cleanup_null); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, "error determining cgi PID (for SSI)"); + } + + apr_pool_cleanup_register(r->pool, info, + cleanup_script, + apr_pool_cleanup_null); + + /* We are putting the socket discriptor into an apr_file_t so that we can + * use a pipe bucket to send the data to the client. APR will create + * a cleanup for the apr_file_t which will close the socket, so we'll + * get rid of the cleanup we registered when we created the socket. + */ + apr_os_pipe_put_ex(&tempsock, &sd, 1, r->pool); + if (dc->timeout > 0) { + apr_file_pipe_timeout_set(tempsock, dc->timeout); + } + else { + apr_file_pipe_timeout_set(tempsock, r->server->timeout); + } + + apr_pool_cleanup_kill(r->pool, (void *)((long)sd), close_unix_socket); + + APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pipe_create(tempsock, + f->c->bucket_alloc)); + ctx->flush_now = 1; + + return APR_SUCCESS; +} + +static apr_status_t handle_exec(include_ctx_t *ctx, ap_filter_t *f, + apr_bucket_brigade *bb) +{ + char *tag = NULL; + char *tag_val = NULL; + request_rec *r = f->r; + char *file = r->filename; + char parsed_string[MAX_STRING_LEN]; + + if (!ctx->argc) { + ap_log_rerror(APLOG_MARK, + (ctx->flags & SSI_FLAG_PRINTING) + ? APLOG_ERR : APLOG_WARNING, + 0, r, APLOGNO(03196) + "missing argument for exec element in %s", r->filename); + } + + if (!(ctx->flags & SSI_FLAG_PRINTING)) { + return APR_SUCCESS; + } + + if (!ctx->argc) { + SSI_CREATE_ERROR_BUCKET(ctx, f, bb); + return APR_SUCCESS; + } + + if (ctx->flags & SSI_FLAG_NO_EXEC) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01271) "exec used but not allowed " + "in %s", r->filename); + SSI_CREATE_ERROR_BUCKET(ctx, f, bb); + return APR_SUCCESS; + } + + while (1) { + cgid_pfn_gtv(ctx, &tag, &tag_val, SSI_VALUE_DECODED); + if (!tag || !tag_val) { + break; + } + + if (!strcmp(tag, "cmd")) { + apr_status_t rv; + + cgid_pfn_ps(ctx, tag_val, parsed_string, sizeof(parsed_string), + SSI_EXPAND_LEAVE_NAME); + + rv = include_cmd(ctx, f, bb, parsed_string); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01272) + "execution failure for parameter \"%s\" " + "to tag exec in file %s", tag, r->filename); + SSI_CREATE_ERROR_BUCKET(ctx, f, bb); + break; + } + } + else if (!strcmp(tag, "cgi")) { + apr_status_t rv; + + cgid_pfn_ps(ctx, tag_val, parsed_string, sizeof(parsed_string), + SSI_EXPAND_DROP_NAME); + + rv = include_cgi(ctx, f, bb, parsed_string); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01273) "invalid CGI ref " + "\"%s\" in %s", tag_val, file); + SSI_CREATE_ERROR_BUCKET(ctx, f, bb); + break; + } + } + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01274) "unknown parameter " + "\"%s\" to tag exec in %s", tag, file); + SSI_CREATE_ERROR_BUCKET(ctx, f, bb); + break; + } + } + + return APR_SUCCESS; +} +/*============================================================================ + *============================================================================ + * This is the end of the cgi filter code moved from mod_include. + *============================================================================ + *============================================================================*/ + + +static void register_hook(apr_pool_t *p) +{ + static const char * const aszPre[] = { "mod_include.c", NULL }; + + ap_hook_pre_config(cgid_pre_config, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_post_config(cgid_init, aszPre, NULL, APR_HOOK_MIDDLE); + ap_hook_handler(cgid_handler, NULL, NULL, APR_HOOK_MIDDLE); +} + +AP_DECLARE_MODULE(cgid) = { + STANDARD20_MODULE_STUFF, + create_cgid_dirconf, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + create_cgid_config, /* server config */ + merge_cgid_config, /* merge server config */ + cgid_cmds, /* command table */ + register_hook /* register_handlers */ +}; + diff --git a/modules/generators/mod_cgid.exp b/modules/generators/mod_cgid.exp new file mode 100644 index 0000000..5f10d48 --- /dev/null +++ b/modules/generators/mod_cgid.exp @@ -0,0 +1 @@ +cgid_module diff --git a/modules/generators/mod_info.c b/modules/generators/mod_info.c new file mode 100644 index 0000000..e5e63de --- /dev/null +++ b/modules/generators/mod_info.c @@ -0,0 +1,1011 @@ +/* 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. + */ + +/* + * Info Module. Display configuration information for the server and + * all included modules. + * + * <Location /server-info> + * SetHandler server-info + * </Location> + * + * GET /server-info - Returns full configuration page for server and all modules + * GET /server-info?server - Returns server configuration only + * GET /server-info?module_name - Returns configuration for a single module + * GET /server-info?list - Returns quick list of included modules + * GET /server-info?config - Returns full configuration + * GET /server-info?hooks - Returns a listing of the modules active for each hook + * + * Original Author: + * Rasmus Lerdorf <rasmus vex.net>, May 1996 + * + * Modified By: + * Lou Langholtz <ldl usi.utah.edu>, July 1997 + * + * Apache 2.0 Port: + * Ryan Morgan <rmorgan covalent.net>, August 2000 + * + */ + + +#include "apr.h" +#include "apr_strings.h" +#include "apr_lib.h" +#include "apr_version.h" +#if APR_MAJOR_VERSION < 2 +#include "apu_version.h" +#endif +#define APR_WANT_STRFUNC +#include "apr_want.h" + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_main.h" +#include "http_protocol.h" +#include "http_connection.h" +#include "http_request.h" +#include "util_script.h" +#include "ap_mpm.h" +#include "mpm_common.h" +#include "ap_provider.h" +#include <stdio.h> +#include <stdlib.h> + +typedef struct +{ + const char *name; /* matching module name */ + const char *info; /* additional info */ +} info_entry; + +typedef struct +{ + apr_array_header_t *more_info; +} info_svr_conf; + +module AP_MODULE_DECLARE_DATA info_module; + +/* current file name when doing -DDUMP_CONFIG */ +const char *dump_config_fn_info; +/* file handle when doing -DDUMP_CONFIG */ +apr_file_t *out = NULL; + +static void *create_info_config(apr_pool_t * p, server_rec * s) +{ + info_svr_conf *conf = + (info_svr_conf *) apr_pcalloc(p, sizeof(info_svr_conf)); + + conf->more_info = apr_array_make(p, 20, sizeof(info_entry)); + return conf; +} + +static void *merge_info_config(apr_pool_t * p, void *basev, void *overridesv) +{ + info_svr_conf *new = + (info_svr_conf *) apr_pcalloc(p, sizeof(info_svr_conf)); + info_svr_conf *base = (info_svr_conf *) basev; + info_svr_conf *overrides = (info_svr_conf *) overridesv; + + new->more_info = + apr_array_append(p, overrides->more_info, base->more_info); + return new; +} + +static void put_int_flush_right(request_rec * r, int i, int field) +{ + if (field > 1 || i > 9) + put_int_flush_right(r, i / 10, field - 1); + if (i) { + if (r) + ap_rputc('0' + i % 10, r); + else + apr_file_putc((char)('0' + i % 10), out); + } + else { + if (r) + ap_rputs(" ", r); + else + apr_file_printf(out, " "); + } +} + +static void set_fn_info(request_rec *r, const char *name) +{ + if (r) + ap_set_module_config(r->request_config, &info_module, (void *)name); + else + dump_config_fn_info = name; +} + +static const char *get_fn_info(request_rec *r) +{ + if (r) + return ap_get_module_config(r->request_config, &info_module); + else + return dump_config_fn_info; +} + + +static void mod_info_indent(request_rec * r, int nest, + const char *thisfn, int linenum) +{ + int i; + const char *prevfn = get_fn_info(r); + if (thisfn == NULL) + thisfn = "*UNKNOWN*"; + if (prevfn == NULL || 0 != strcmp(prevfn, thisfn)) { + if (r) { + thisfn = ap_escape_html(r->pool, thisfn); + ap_rprintf(r, "<dd><tt><strong>In file: %s</strong></tt></dd>\n", + thisfn); + } + else { + apr_file_printf(out, "# In file: %s\n", thisfn); + } + set_fn_info(r, thisfn); + } + + if (r) { + ap_rputs("<dd><tt>", r); + put_int_flush_right(r, linenum > 0 ? linenum : 0, 4); + ap_rputs(": ", r); + } + else if (linenum > 0) { + for (i = 1; i <= nest; ++i) + apr_file_printf(out, " "); + apr_file_putc('#', out); + put_int_flush_right(r, linenum, 4); + apr_file_printf(out, ":\n"); + } + + for (i = 1; i <= nest; ++i) { + if (r) + ap_rputs("  ", r); + else + apr_file_printf(out, " "); + } +} + +static void mod_info_show_cmd(request_rec * r, const ap_directive_t * dir, + int nest) +{ + mod_info_indent(r, nest, dir->filename, dir->line_num); + if (r) + ap_rprintf(r, "%s <i>%s</i></tt></dd>\n", + ap_escape_html(r->pool, dir->directive), + ap_escape_html(r->pool, dir->args)); + else + apr_file_printf(out, "%s %s\n", dir->directive, dir->args); +} + +static void mod_info_show_open(request_rec * r, const ap_directive_t * dir, + int nest) +{ + mod_info_indent(r, nest, dir->filename, dir->line_num); + if (r) + ap_rprintf(r, "%s %s</tt></dd>\n", + ap_escape_html(r->pool, dir->directive), + ap_escape_html(r->pool, dir->args)); + else + apr_file_printf(out, "%s %s\n", dir->directive, dir->args); +} + +static void mod_info_show_close(request_rec * r, const ap_directive_t * dir, + int nest) +{ + const char *dirname = dir->directive; + mod_info_indent(r, nest, dir->filename, 0); + if (*dirname == '<') { + if (r) + ap_rprintf(r, "</%s></tt></dd>", + ap_escape_html(r->pool, dirname + 1)); + else + apr_file_printf(out, "</%s>\n", dirname + 1); + } + else { + if (r) + ap_rprintf(r, "/%s</tt></dd>", ap_escape_html(r->pool, dirname)); + else + apr_file_printf(out, "/%s\n", dirname); + } +} + +static int mod_info_has_cmd(const command_rec * cmds, ap_directive_t * dir) +{ + const command_rec *cmd; + if (cmds == NULL) + return 1; + for (cmd = cmds; cmd->name; ++cmd) { + if (strcasecmp(cmd->name, dir->directive) == 0) + return 1; + } + return 0; +} + +static void mod_info_show_parents(request_rec * r, ap_directive_t * node, + int from, int to) +{ + if (from < to) + mod_info_show_parents(r, node->parent, from, to - 1); + mod_info_show_open(r, node, to); +} + +static int mod_info_module_cmds(request_rec * r, const command_rec * cmds, + ap_directive_t * node, int from, int level) +{ + int shown = from; + ap_directive_t *dir; + if (level == 0) + set_fn_info(r, NULL); + for (dir = node; dir; dir = dir->next) { + if (dir->first_child != NULL) { + if (level < mod_info_module_cmds(r, cmds, dir->first_child, + shown, level + 1)) { + shown = level; + mod_info_show_close(r, dir, level); + } + } + else if (mod_info_has_cmd(cmds, dir)) { + if (shown < level) { + mod_info_show_parents(r, dir->parent, shown, level - 1); + shown = level; + } + mod_info_show_cmd(r, dir, level); + } + } + return shown; +} + +typedef struct +{ /*XXX: should get something from apr_hooks.h instead */ + void (*pFunc) (void); /* just to get the right size */ + const char *szName; + const char *const *aszPredecessors; + const char *const *aszSuccessors; + int nOrder; +} hook_struct_t; + +/* + * hook_get_t is a pointer to a function that takes void as an argument and + * returns a pointer to an apr_array_header_t. The nasty WIN32 ifdef + * is required to account for the fact that the ap_hook* calls all use + * STDCALL calling convention. + */ +typedef apr_array_header_t *( +#ifdef WIN32 + __stdcall +#endif + * hook_get_t) (void); + +typedef struct +{ + const char *name; + hook_get_t get; +} hook_lookup_t; + +static hook_lookup_t startup_hooks[] = { + {"Pre-Config", ap_hook_get_pre_config}, + {"Check Configuration", ap_hook_get_check_config}, + {"Test Configuration", ap_hook_get_test_config}, + {"Post Configuration", ap_hook_get_post_config}, + {"Open Logs", ap_hook_get_open_logs}, + {"Pre-MPM", ap_hook_get_pre_mpm}, + {"MPM", ap_hook_get_mpm}, + {"Drop Privileges", ap_hook_get_drop_privileges}, + {"Retrieve Optional Functions", ap_hook_get_optional_fn_retrieve}, + {"Child Init", ap_hook_get_child_init}, + {NULL}, +}; + +static hook_lookup_t request_hooks[] = { + {"Pre-Connection", ap_hook_get_pre_connection}, + {"Create Connection", ap_hook_get_create_connection}, + {"Process Connection", ap_hook_get_process_connection}, + {"Create Request", ap_hook_get_create_request}, + {"Pre-Read Request", ap_hook_get_pre_read_request}, + {"Post-Read Request", ap_hook_get_post_read_request}, + {"Header Parse", ap_hook_get_header_parser}, + {"HTTP Scheme", ap_hook_get_http_scheme}, + {"Default Port", ap_hook_get_default_port}, + {"Quick Handler", ap_hook_get_quick_handler}, + {"Translate Name", ap_hook_get_translate_name}, + {"Map to Storage", ap_hook_get_map_to_storage}, + {"Check Access", ap_hook_get_access_checker_ex}, + {"Check Access (legacy)", ap_hook_get_access_checker}, + {"Verify User ID", ap_hook_get_check_user_id}, + {"Note Authentication Failure", ap_hook_get_note_auth_failure}, + {"Verify User Access", ap_hook_get_auth_checker}, + {"Check Type", ap_hook_get_type_checker}, + {"Fixups", ap_hook_get_fixups}, + {"Insert Filters", ap_hook_get_insert_filter}, + {"Content Handlers", ap_hook_get_handler}, + {"Transaction Logging", ap_hook_get_log_transaction}, + {"Insert Errors", ap_hook_get_insert_error_filter}, + {"Generate Log ID", ap_hook_get_generate_log_id}, + {NULL}, +}; + +static hook_lookup_t other_hooks[] = { + {"Monitor", ap_hook_get_monitor}, + {"Child Status", ap_hook_get_child_status}, + {"End Generation", ap_hook_get_end_generation}, + {"Error Logging", ap_hook_get_error_log}, + {"Query MPM Attributes", ap_hook_get_mpm_query}, + {"Query MPM Name", ap_hook_get_mpm_get_name}, + {"Register Timed Callback", ap_hook_get_mpm_register_timed_callback}, + {"Extend Expression Parser", ap_hook_get_expr_lookup}, + {"Set Management Items", ap_hook_get_get_mgmt_items}, +#if AP_ENABLE_EXCEPTION_HOOK + {"Handle Fatal Exceptions", ap_hook_get_fatal_exception}, +#endif + {NULL}, +}; + +static int module_find_hook(module * modp, hook_get_t hook_get) +{ + int i; + apr_array_header_t *hooks = hook_get(); + hook_struct_t *elts; + + if (!hooks) { + return 0; + } + + elts = (hook_struct_t *) hooks->elts; + + for (i = 0; i < hooks->nelts; i++) { + if (strcmp(elts[i].szName, modp->name) == 0) { + return 1; + } + } + + return 0; +} + +static void module_participate(request_rec * r, + module * modp, + hook_lookup_t * lookup, int *comma) +{ + if (module_find_hook(modp, lookup->get)) { + if (*comma) { + ap_rputs(", ", r); + } + ap_rvputs(r, "<tt>", lookup->name, "</tt>", NULL); + *comma = 1; + } +} + +static void module_request_hook_participate(request_rec * r, module * modp) +{ + int i, comma = 0; + + ap_rputs("<dt><strong>Request Phase Participation:</strong>\n", r); + + for (i = 0; request_hooks[i].name; i++) { + module_participate(r, modp, &request_hooks[i], &comma); + } + + if (!comma) { + ap_rputs("<tt> <em>none</em></tt>", r); + } + ap_rputs("</dt>\n", r); +} + +static const char *find_more_info(server_rec * s, const char *module_name) +{ + int i; + info_svr_conf *conf = + (info_svr_conf *) ap_get_module_config(s->module_config, + &info_module); + info_entry *entry = (info_entry *) conf->more_info->elts; + + if (!module_name) { + return 0; + } + for (i = 0; i < conf->more_info->nelts; i++) { + if (!strcmp(module_name, entry->name)) { + return entry->info; + } + entry++; + } + return 0; +} + +static int show_server_settings(request_rec * r) +{ + server_rec *serv = r->server; + int max_daemons, forked, threaded; + + ap_rputs("<h2><a name=\"server\">Server Settings</a></h2>", r); + ap_rprintf(r, + "<dl><dt><strong>Server Version:</strong> " + "<font size=\"+1\"><tt>%s</tt></font></dt>\n", + ap_get_server_description()); + ap_rprintf(r, + "<dt><strong>Server Built:</strong> " + "<font size=\"+1\"><tt>%s</tt></font></dt>\n", + ap_get_server_built()); + ap_rprintf(r, + "<dt><strong>Server loaded APR Version:</strong> " + "<tt>%s</tt></dt>\n", apr_version_string()); + ap_rprintf(r, + "<dt><strong>Compiled with APR Version:</strong> " + "<tt>%s</tt></dt>\n", APR_VERSION_STRING); +#if APR_MAJOR_VERSION < 2 + ap_rprintf(r, + "<dt><strong>Server loaded APU Version:</strong> " + "<tt>%s</tt></dt>\n", apu_version_string()); + ap_rprintf(r, + "<dt><strong>Compiled with APU Version:</strong> " + "<tt>%s</tt></dt>\n", APU_VERSION_STRING); +#endif + ap_rprintf(r, + "<dt><strong>Module Magic Number:</strong> " + "<tt>%d:%d</tt></dt>\n", MODULE_MAGIC_NUMBER_MAJOR, + MODULE_MAGIC_NUMBER_MINOR); + ap_rprintf(r, + "<dt><strong>Hostname/port:</strong> " + "<tt>%s:%u</tt></dt>\n", + ap_escape_html(r->pool, ap_get_server_name(r)), + ap_get_server_port(r)); + ap_rprintf(r, + "<dt><strong>Timeouts:</strong> " + "<tt>connection: %d    " + "keep-alive: %d</tt></dt>", + (int) (apr_time_sec(serv->timeout)), + (int) (apr_time_sec(serv->keep_alive_timeout))); + ap_mpm_query(AP_MPMQ_MAX_DAEMON_USED, &max_daemons); + ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded); + ap_mpm_query(AP_MPMQ_IS_FORKED, &forked); + ap_rprintf(r, "<dt><strong>MPM Name:</strong> <tt>%s</tt></dt>\n", + ap_show_mpm()); + ap_rprintf(r, + "<dt><strong>MPM Information:</strong> " + "<tt>Max Daemons: %d Threaded: %s Forked: %s</tt></dt>\n", + max_daemons, threaded ? "yes" : "no", forked ? "yes" : "no"); + ap_rprintf(r, + "<dt><strong>Server Architecture:</strong> " + "<tt>%ld-bit</tt></dt>\n", 8 * (long) sizeof(void *)); + ap_rprintf(r, + "<dt><strong>Server Root:</strong> " + "<tt>%s</tt></dt>\n", ap_server_root); + ap_rprintf(r, + "<dt><strong>Config File:</strong> " + "<tt>%s</tt></dt>\n", ap_conftree->filename); + + ap_rputs("<dt><strong>Server Built With:</strong>\n" + "<tt style=\"white-space: pre;\">\n", r); + + /* TODO: Not all of these defines are getting set like they do in main.c. + * Missing some headers? + */ + +#ifdef BIG_SECURITY_HOLE + ap_rputs(" -D BIG_SECURITY_HOLE\n", r); +#endif + +#ifdef SECURITY_HOLE_PASS_AUTHORIZATION + ap_rputs(" -D SECURITY_HOLE_PASS_AUTHORIZATION\n", r); +#endif + +#ifdef OS + ap_rputs(" -D OS=\"" OS "\"\n", r); +#endif + +#ifdef HAVE_SHMGET + ap_rputs(" -D HAVE_SHMGET\n", r); +#endif + +#if APR_FILE_BASED_SHM + ap_rputs(" -D APR_FILE_BASED_SHM\n", r); +#endif + +#if APR_HAS_SENDFILE + ap_rputs(" -D APR_HAS_SENDFILE\n", r); +#endif + +#if APR_HAS_MMAP + ap_rputs(" -D APR_HAS_MMAP\n", r); +#endif + +#ifdef NO_WRITEV + ap_rputs(" -D NO_WRITEV\n", r); +#endif + +#ifdef NO_LINGCLOSE + ap_rputs(" -D NO_LINGCLOSE\n", r); +#endif + +#if APR_HAVE_IPV6 + ap_rputs(" -D APR_HAVE_IPV6 (IPv4-mapped addresses ", r); +#ifdef AP_ENABLE_V4_MAPPED + ap_rputs("enabled)\n", r); +#else + ap_rputs("disabled)\n", r); +#endif +#endif + +#if APR_USE_FLOCK_SERIALIZE + ap_rputs(" -D APR_USE_FLOCK_SERIALIZE\n", r); +#endif + +#if APR_USE_SYSVSEM_SERIALIZE + ap_rputs(" -D APR_USE_SYSVSEM_SERIALIZE\n", r); +#endif + +#if APR_USE_POSIXSEM_SERIALIZE + ap_rputs(" -D APR_USE_POSIXSEM_SERIALIZE\n", r); +#endif + +#if APR_USE_FCNTL_SERIALIZE + ap_rputs(" -D APR_USE_FCNTL_SERIALIZE\n", r); +#endif + +#if APR_USE_PROC_PTHREAD_SERIALIZE + ap_rputs(" -D APR_USE_PROC_PTHREAD_SERIALIZE\n", r); +#endif +#if APR_PROCESS_LOCK_IS_GLOBAL + ap_rputs(" -D APR_PROCESS_LOCK_IS_GLOBAL\n", r); +#endif + +#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT + ap_rputs(" -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT\n", r); +#endif + +#if APR_HAS_OTHER_CHILD + ap_rputs(" -D APR_HAS_OTHER_CHILD\n", r); +#endif + +#ifdef AP_HAVE_RELIABLE_PIPED_LOGS + ap_rputs(" -D AP_HAVE_RELIABLE_PIPED_LOGS\n", r); +#endif + +#ifdef BUFFERED_LOGS + ap_rputs(" -D BUFFERED_LOGS\n", r); +#ifdef PIPE_BUF + ap_rputs(" -D PIPE_BUF=%ld\n", (long) PIPE_BUF, r); +#endif +#endif + +#if APR_CHARSET_EBCDIC + ap_rputs(" -D APR_CHARSET_EBCDIC\n", r); +#endif + +#ifdef NEED_HASHBANG_EMUL + ap_rputs(" -D NEED_HASHBANG_EMUL\n", r); +#endif + +/* This list displays the compiled in default paths: */ +#ifdef HTTPD_ROOT + ap_rputs(" -D HTTPD_ROOT=\"" HTTPD_ROOT "\"\n", r); +#endif + +#ifdef SUEXEC_BIN + ap_rputs(" -D SUEXEC_BIN=\"" SUEXEC_BIN "\"\n", r); +#endif + +#ifdef DEFAULT_PIDLOG + ap_rputs(" -D DEFAULT_PIDLOG=\"" DEFAULT_PIDLOG "\"\n", r); +#endif + +#ifdef DEFAULT_SCOREBOARD + ap_rputs(" -D DEFAULT_SCOREBOARD=\"" DEFAULT_SCOREBOARD "\"\n", r); +#endif + +#ifdef DEFAULT_ERRORLOG + ap_rputs(" -D DEFAULT_ERRORLOG=\"" DEFAULT_ERRORLOG "\"\n", r); +#endif + + +#ifdef AP_TYPES_CONFIG_FILE + ap_rputs(" -D AP_TYPES_CONFIG_FILE=\"" AP_TYPES_CONFIG_FILE "\"\n", r); +#endif + +#ifdef SERVER_CONFIG_FILE + ap_rputs(" -D SERVER_CONFIG_FILE=\"" SERVER_CONFIG_FILE "\"\n", r); +#endif + ap_rputs("</tt></dt>\n", r); + ap_rputs("</dl><hr />", r); + return 0; +} + +static int dump_a_hook(request_rec * r, hook_get_t hook_get) +{ + int i; + char qs; + hook_struct_t *elts; + apr_array_header_t *hooks = hook_get(); + + if (!hooks) { + return 0; + } + + if (r->args && strcasecmp(r->args, "hooks") == 0) { + qs = '?'; + } + else { + qs = '#'; + } + + elts = (hook_struct_t *) hooks->elts; + + for (i = 0; i < hooks->nelts; i++) { + ap_rprintf(r, + "   %02d <a href=\"%c%s\">%s</a> <br/>", + elts[i].nOrder, qs, elts[i].szName, elts[i].szName); + } + return 0; +} + +static int show_active_hooks(request_rec * r) +{ + int i; + ap_rputs("<h2><a name=\"startup_hooks\">Startup Hooks</a></h2>\n<dl>", r); + + for (i = 0; startup_hooks[i].name; i++) { + ap_rprintf(r, "<dt><strong>%s:</strong>\n <br /><tt>\n", + startup_hooks[i].name); + dump_a_hook(r, startup_hooks[i].get); + ap_rputs("\n </tt>\n</dt>\n", r); + } + + ap_rputs + ("</dl>\n<hr />\n<h2><a name=\"request_hooks\">Request Hooks</a></h2>\n<dl>", + r); + + for (i = 0; request_hooks[i].name; i++) { + ap_rprintf(r, "<dt><strong>%s:</strong>\n <br /><tt>\n", + request_hooks[i].name); + dump_a_hook(r, request_hooks[i].get); + ap_rputs("\n </tt>\n</dt>\n", r); + } + + ap_rputs + ("</dl>\n<hr />\n<h2><a name=\"other_hooks\">Other Hooks</a></h2>\n<dl>", + r); + + for (i = 0; other_hooks[i].name; i++) { + ap_rprintf(r, "<dt><strong>%s:</strong>\n <br /><tt>\n", + other_hooks[i].name); + dump_a_hook(r, other_hooks[i].get); + ap_rputs("\n </tt>\n</dt>\n", r); + } + + ap_rputs("</dl>\n<hr />\n", r); + + return 0; +} + +static int cmp_provider_groups(const void *a_, const void *b_) +{ + const ap_list_provider_groups_t *a = a_, *b = b_; + int ret = strcmp(a->provider_group, b->provider_group); + if (!ret) + ret = strcmp(a->provider_version, b->provider_version); + return ret; +} + +static int cmp_provider_names(const void *a_, const void *b_) +{ + const ap_list_provider_names_t *a = a_, *b = b_; + return strcmp(a->provider_name, b->provider_name); +} + +static void show_providers(request_rec *r) +{ + apr_array_header_t *groups = ap_list_provider_groups(r->pool); + ap_list_provider_groups_t *group; + apr_array_header_t *names; + ap_list_provider_names_t *name; + int i,j; + const char *cur_group = NULL; + + qsort(groups->elts, groups->nelts, sizeof(ap_list_provider_groups_t), + cmp_provider_groups); + ap_rputs("<h2><a name=\"providers\">Providers</a></h2>\n<dl>", r); + + for (i = 0; i < groups->nelts; i++) { + group = &APR_ARRAY_IDX(groups, i, ap_list_provider_groups_t); + if (!cur_group || strcmp(cur_group, group->provider_group) != 0) { + if (cur_group) + ap_rputs("\n</dt>\n", r); + cur_group = group->provider_group; + ap_rprintf(r, "<dt><strong>%s</strong> (version <tt>%s</tt>):" + "\n <br />\n", cur_group, group->provider_version); + } + names = ap_list_provider_names(r->pool, group->provider_group, + group->provider_version); + qsort(names->elts, names->nelts, sizeof(ap_list_provider_names_t), + cmp_provider_names); + for (j = 0; j < names->nelts; j++) { + name = &APR_ARRAY_IDX(names, j, ap_list_provider_names_t); + ap_rprintf(r, "<tt>  %s</tt><br/>", name->provider_name); + } + } + if (cur_group) + ap_rputs("\n</dt>\n", r); + ap_rputs("</dl>\n<hr />\n", r); +} + +static int cmp_module_name(const void *a_, const void *b_) +{ + const module * const *a = a_; + const module * const *b = b_; + return strcmp((*a)->name, (*b)->name); +} + +static apr_array_header_t *get_sorted_modules(apr_pool_t *p) +{ + apr_array_header_t *arr = apr_array_make(p, 64, sizeof(module *)); + module *modp, **entry; + for (modp = ap_top_module; modp; modp = modp->next) { + entry = &APR_ARRAY_PUSH(arr, module *); + *entry = modp; + } + qsort(arr->elts, arr->nelts, sizeof(module *), cmp_module_name); + return arr; +} + +static int display_info(request_rec * r) +{ + module *modp = NULL; + const char *more_info; + const command_rec *cmd; + apr_array_header_t *modules = NULL; + int i; + + if (strcmp(r->handler, "server-info")) { + return DECLINED; + } + + r->allowed |= (AP_METHOD_BIT << M_GET); + if (r->method_number != M_GET) { + return DECLINED; + } + + ap_set_content_type(r, "text/html; charset=ISO-8859-1"); + + ap_rputs(DOCTYPE_XHTML_1_0T + "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" + "<head>\n" + " <title>Server Information\n" "\n", r); + ap_rputs("

" + "Apache Server Information

\n", r); + if (!r->args || strcasecmp(r->args, "list")) { + if (!r->args) { + ap_rputs("
Subpages:
", r); + ap_rputs("Configuration Files, " + "Server Settings, " + "Module List, " + "Active Hooks, " + "Available Providers", r); + ap_rputs("

", r); + + ap_rputs("
Sections:
", r); + ap_rputs("Loaded Modules, " + "Server Settings, " + "Startup Hooks, " + "Request Hooks, " + "Other Hooks, " + "Providers", r); + ap_rputs("

", r); + + ap_rputs("

Loaded Modules

" + "
", r); + + modules = get_sorted_modules(r->pool); + for (i = 0; i < modules->nelts; i++) { + modp = APR_ARRAY_IDX(modules, i, module *); + ap_rprintf(r, "%s", modp->name, + modp->name); + if (i < modules->nelts) { + ap_rputs(", ", r); + } + } + ap_rputs("

", r); + } + + if (!r->args || !strcasecmp(r->args, "server")) { + show_server_settings(r); + } + + if (!r->args || !strcasecmp(r->args, "hooks")) { + show_active_hooks(r); + } + + if (!r->args || !strcasecmp(r->args, "providers")) { + show_providers(r); + } + + if (r->args && 0 == strcasecmp(r->args, "config")) { + ap_rputs("
Configuration:\n", r); + mod_info_module_cmds(r, NULL, ap_conftree, 0, 0); + ap_rputs("

", r); + } + else { + int comma = 0; + if (!modules) + modules = get_sorted_modules(r->pool); + for (i = 0; i < modules->nelts; i++) { + modp = APR_ARRAY_IDX(modules, i, module *); + if (!r->args || !strcasecmp(modp->name, r->args)) { + ap_rprintf(r, + "
Module Name: " + "%s
\n", + modp->name, modp->name, modp->name); + ap_rputs("
Content handlers: ", r); + + if (module_find_hook(modp, ap_hook_get_handler)) { + ap_rputs(" yes", r); + } + else { + ap_rputs(" none", r); + } + + ap_rputs("
", r); + ap_rputs + ("
Configuration Phase Participation:\n", + r); + if (modp->create_dir_config) { + if (comma) { + ap_rputs(", ", r); + } + ap_rputs("Create Directory Config", r); + comma = 1; + } + if (modp->merge_dir_config) { + if (comma) { + ap_rputs(", ", r); + } + ap_rputs("Merge Directory Configs", r); + comma = 1; + } + if (modp->create_server_config) { + if (comma) { + ap_rputs(", ", r); + } + ap_rputs("Create Server Config", r); + comma = 1; + } + if (modp->merge_server_config) { + if (comma) { + ap_rputs(", ", r); + } + ap_rputs("Merge Server Configs", r); + comma = 1; + } + if (!comma) + ap_rputs(" none", r); + comma = 0; + ap_rputs("
", r); + + module_request_hook_participate(r, modp); + + cmd = modp->cmds; + if (cmd) { + ap_rputs + ("
Module Directives:
", + r); + while (cmd) { + if (cmd->name) { + ap_rprintf(r, "
%s%s - ", + ap_escape_html(r->pool, cmd->name), + cmd->name[0] == '<' ? ">" : ""); + if (cmd->errmsg) { + ap_rputs(ap_escape_html(r->pool, cmd->errmsg), r); + } + ap_rputs("
\n", r); + } + else { + break; + } + cmd++; + } + ap_rputs + ("
Current Configuration:
\n", + r); + mod_info_module_cmds(r, modp->cmds, ap_conftree, 0, + 0); + } + else { + ap_rputs + ("
Module Directives: none
", + r); + } + more_info = find_more_info(r->server, modp->name); + if (more_info) { + ap_rputs + ("
Additional Information:\n
", + r); + ap_rputs(more_info, r); + ap_rputs("
", r); + } + ap_rputs("

\n", r); + if (r->args) { + break; + } + } + } + if (!modp && r->args && strcasecmp(r->args, "server")) { + ap_rputs("

No such module

\n", r); + } + } + } + else { + ap_rputs("
Server Module List
", r); + modules = get_sorted_modules(r->pool); + for (i = 0; i < modules->nelts; i++) { + modp = APR_ARRAY_IDX(modules, i, module *); + ap_rputs("
", r); + ap_rputs(modp->name, r); + ap_rputs("
", r); + } + ap_rputs("

", r); + } + ap_rputs(ap_psignature("", r), r); + ap_rputs("\n", r); + /* Done, turn off timeout, close file and return */ + return 0; +} + +static const char *add_module_info(cmd_parms * cmd, void *dummy, + const char *name, const char *info) +{ + server_rec *s = cmd->server; + info_svr_conf *conf = + (info_svr_conf *) ap_get_module_config(s->module_config, + &info_module); + info_entry *new = apr_array_push(conf->more_info); + + new->name = name; + new->info = info; + return NULL; +} + +static const command_rec info_cmds[] = { + AP_INIT_TAKE2("AddModuleInfo", add_module_info, NULL, RSRC_CONF, + "a module name and additional information on that module"), + {NULL} +}; + +static int check_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, + server_rec *s) +{ + if (ap_exists_config_define("DUMP_CONFIG")) { + apr_file_open_stdout(&out, ptemp); + mod_info_module_cmds(NULL, NULL, ap_conftree, 0, 0); + } + + return DECLINED; +} + + +static void register_hooks(apr_pool_t * p) +{ + ap_hook_handler(display_info, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_check_config(check_config, NULL, NULL, APR_HOOK_FIRST); +} + +AP_DECLARE_MODULE(info) = { + STANDARD20_MODULE_STUFF, + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + create_info_config, /* server config */ + merge_info_config, /* merge server config */ + info_cmds, /* command apr_table_t */ + register_hooks +}; diff --git a/modules/generators/mod_info.dep b/modules/generators/mod_info.dep new file mode 100644 index 0000000..cdfc02b --- /dev/null +++ b/modules/generators/mod_info.dep @@ -0,0 +1,66 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_info.mak + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + + +.\mod_info.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_mpm.h"\ + "..\..\include\ap_provider.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_connection.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\httpd.h"\ + "..\..\include\mpm_common.h"\ + "..\..\include\os.h"\ + "..\..\include\scoreboard.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_script.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr-util\include\apu_version.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_version.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + diff --git a/modules/generators/mod_info.dsp b/modules/generators/mod_info.dsp new file mode 100644 index 0000000..524ebf8 --- /dev/null +++ b/modules/generators/mod_info.dsp @@ -0,0 +1,111 @@ +# Microsoft Developer Studio Project File - Name="mod_info" - 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_info - 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_info.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_info.mak" CFG="mod_info - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_info - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_info - 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_info - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_info_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_info.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_info.so" /d LONG_NAME="info_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:".\Release\mod_info.so" /base:@..\..\os\win32\BaseAddr.ref,mod_info.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_info.so" /base:@..\..\os\win32\BaseAddr.ref,mod_info.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_info.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_info - 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_info_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_info.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_info.so" /d LONG_NAME="info_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_info.so" /base:@..\..\os\win32\BaseAddr.ref,mod_info.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_info.so" /base:@..\..\os\win32\BaseAddr.ref,mod_info.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_info.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_info - Win32 Release" +# Name "mod_info - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_info.c +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/generators/mod_info.exp b/modules/generators/mod_info.exp new file mode 100644 index 0000000..c304fa7 --- /dev/null +++ b/modules/generators/mod_info.exp @@ -0,0 +1 @@ +info_module diff --git a/modules/generators/mod_info.mak b/modules/generators/mod_info.mak new file mode 100644 index 0000000..b0de8a1 --- /dev/null +++ b/modules/generators/mod_info.mak @@ -0,0 +1,353 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_info.dsp +!IF "$(CFG)" == "" +CFG=mod_info - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_info - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_info - Win32 Release" && "$(CFG)" != "mod_info - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_info.mak" CFG="mod_info - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_info - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_info - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_info - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_info.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_info.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_info.obj" + -@erase "$(INTDIR)\mod_info.res" + -@erase "$(INTDIR)\mod_info_src.idb" + -@erase "$(INTDIR)\mod_info_src.pdb" + -@erase "$(OUTDIR)\mod_info.exp" + -@erase "$(OUTDIR)\mod_info.lib" + -@erase "$(OUTDIR)\mod_info.pdb" + -@erase "$(OUTDIR)\mod_info.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_info_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_info.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_info.so" /d LONG_NAME="info_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_info.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_info.pdb" /debug /out:"$(OUTDIR)\mod_info.so" /implib:"$(OUTDIR)\mod_info.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_info.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_info.obj" \ + "$(INTDIR)\mod_info.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" + +"$(OUTDIR)\mod_info.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_info.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_info.so" + if exist .\Release\mod_info.so.manifest mt.exe -manifest .\Release\mod_info.so.manifest -outputresource:.\Release\mod_info.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_info - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_info.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_info.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_info.obj" + -@erase "$(INTDIR)\mod_info.res" + -@erase "$(INTDIR)\mod_info_src.idb" + -@erase "$(INTDIR)\mod_info_src.pdb" + -@erase "$(OUTDIR)\mod_info.exp" + -@erase "$(OUTDIR)\mod_info.lib" + -@erase "$(OUTDIR)\mod_info.pdb" + -@erase "$(OUTDIR)\mod_info.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_info_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_info.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_info.so" /d LONG_NAME="info_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_info.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_info.pdb" /debug /out:"$(OUTDIR)\mod_info.so" /implib:"$(OUTDIR)\mod_info.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_info.so +LINK32_OBJS= \ + "$(INTDIR)\mod_info.obj" \ + "$(INTDIR)\mod_info.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" + +"$(OUTDIR)\mod_info.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_info.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_info.so" + if exist .\Debug\mod_info.so.manifest mt.exe -manifest .\Debug\mod_info.so.manifest -outputresource:.\Debug\mod_info.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_info.dep") +!INCLUDE "mod_info.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_info.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_info - Win32 Release" || "$(CFG)" == "mod_info - Win32 Debug" + +!IF "$(CFG)" == "mod_info - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\generators" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\generators" + +!ELSEIF "$(CFG)" == "mod_info - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\generators" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\generators" + +!ENDIF + +!IF "$(CFG)" == "mod_info - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\generators" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\generators" + +!ELSEIF "$(CFG)" == "mod_info - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\generators" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\generators" + +!ENDIF + +!IF "$(CFG)" == "mod_info - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\generators" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\generators" + +!ELSEIF "$(CFG)" == "mod_info - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\generators" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\generators" + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_info - Win32 Release" + + +"$(INTDIR)\mod_info.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_info.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_info.so" /d LONG_NAME="info_module for Apache" $(SOURCE) + + +!ELSEIF "$(CFG)" == "mod_info - Win32 Debug" + + +"$(INTDIR)\mod_info.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_info.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_info.so" /d LONG_NAME="info_module for Apache" $(SOURCE) + + +!ENDIF + +SOURCE=.\mod_info.c + +"$(INTDIR)\mod_info.obj" : $(SOURCE) "$(INTDIR)" + + + +!ENDIF + diff --git a/modules/generators/mod_status.c b/modules/generators/mod_status.c new file mode 100644 index 0000000..5917953 --- /dev/null +++ b/modules/generators/mod_status.c @@ -0,0 +1,1051 @@ +/* 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. + */ + +/* Status Module. Display lots of internal data about how Apache is + * performing and the state of all children processes. + * + * To enable this, add the following lines into any config file: + * + * + * SetHandler server-status + * + * + * You may want to protect this location by password or domain so no one + * else can look at it. Then you can access the statistics with a URL like: + * + * http://your_server_name/server-status + * + * /server-status - Returns page using tables + * /server-status?notable - Returns page for browsers without table support + * /server-status?refresh - Returns page with 1 second refresh + * /server-status?refresh=6 - Returns page with refresh every 6 seconds + * /server-status?auto - Returns page with data for automatic parsing + * + * Mark Cox, mark@ukweb.com, November 1995 + * + * 12.11.95 Initial version for www.telescope.org + * 13.3.96 Updated to remove rprintf's [Mark] + * 18.3.96 Added CPU usage, process information, and tidied [Ben Laurie] + * 18.3.96 Make extra Scoreboard variables #definable + * 25.3.96 Make short report have full precision [Ben Laurie suggested] + * 25.3.96 Show uptime better [Mark/Ben Laurie] + * 29.3.96 Better HTML and explanation [Mark/Rob Hartill suggested] + * 09.4.96 Added message for non-STATUS compiled version + * 18.4.96 Added per child and per slot counters [Jim Jagielski] + * 01.5.96 Table format, cleanup, even more spiffy data [Chuck Murcko/Jim J.] + * 18.5.96 Adapted to use new rprintf() routine, incidentally fixing a missing + * piece in short reports [Ben Laurie] + * 21.5.96 Additional Status codes (DNS and LOGGING only enabled if + * extended STATUS is enabled) [George Burgyan/Jim J.] + * 10.8.98 Allow for extended status info at runtime (no more STATUS) + * [Jim J.] + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_main.h" +#include "ap_mpm.h" +#include "util_script.h" +#include +#include "scoreboard.h" +#include "http_log.h" +#include "mod_status.h" +#if APR_HAVE_UNISTD_H +#include +#endif +#define APR_WANT_STRFUNC +#include "apr_want.h" +#include "apr_strings.h" + +#define STATUS_MAXLINE 64 + +#define KBYTE 1024 +#define MBYTE 1048576L +#define GBYTE 1073741824L + +#ifndef DEFAULT_TIME_FORMAT +#define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z" +#endif + +#define STATUS_MAGIC_TYPE "application/x-httpd-status" + +module AP_MODULE_DECLARE_DATA status_module; + +static int server_limit, thread_limit, threads_per_child, max_servers, + is_async; + +/* Implement 'ap_run_status_hook'. */ +APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ap, STATUS, int, status_hook, + (request_rec *r, int flags), + (r, flags), + OK, DECLINED) + +#ifdef HAVE_TIMES +/* ugh... need to know if we're running with a pthread implementation + * such as linuxthreads that treats individual threads as distinct + * processes; that affects how we add up CPU time in a process + */ +static pid_t child_pid; +#endif + +/* Format the number of bytes nicely */ +static void format_byte_out(request_rec *r, apr_off_t bytes) +{ + if (bytes < (5 * KBYTE)) + ap_rprintf(r, "%d B", (int) bytes); + else if (bytes < (MBYTE / 2)) + ap_rprintf(r, "%.1f kB", (float) bytes / KBYTE); + else if (bytes < (GBYTE / 2)) + ap_rprintf(r, "%.1f MB", (float) bytes / MBYTE); + else + ap_rprintf(r, "%.1f GB", (float) bytes / GBYTE); +} + +static void format_kbyte_out(request_rec *r, apr_off_t kbytes) +{ + if (kbytes < KBYTE) + ap_rprintf(r, "%d kB", (int) kbytes); + else if (kbytes < MBYTE) + ap_rprintf(r, "%.1f MB", (float) kbytes / KBYTE); + else + ap_rprintf(r, "%.1f GB", (float) kbytes / MBYTE); +} + +static void show_time(request_rec *r, apr_uint32_t tsecs) +{ + int days, hrs, mins, secs; + + secs = (int)(tsecs % 60); + tsecs /= 60; + mins = (int)(tsecs % 60); + tsecs /= 60; + hrs = (int)(tsecs % 24); + days = (int)(tsecs / 24); + + if (days) + ap_rprintf(r, " %d day%s", days, days == 1 ? "" : "s"); + + if (hrs) + ap_rprintf(r, " %d hour%s", hrs, hrs == 1 ? "" : "s"); + + if (mins) + ap_rprintf(r, " %d minute%s", mins, mins == 1 ? "" : "s"); + + if (secs) + ap_rprintf(r, " %d second%s", secs, secs == 1 ? "" : "s"); +} + +/* Main handler for x-httpd-status requests */ + +/* ID values for command table */ + +#define STAT_OPT_END -1 +#define STAT_OPT_REFRESH 0 +#define STAT_OPT_NOTABLE 1 +#define STAT_OPT_AUTO 2 + +struct stat_opt { + int id; + const char *form_data_str; + const char *hdr_out_str; +}; + +static const struct stat_opt status_options[] = /* see #defines above */ +{ + {STAT_OPT_REFRESH, "refresh", "Refresh"}, + {STAT_OPT_NOTABLE, "notable", NULL}, + {STAT_OPT_AUTO, "auto", NULL}, + {STAT_OPT_END, NULL, NULL} +}; + +/* add another state for slots above the MaxRequestWorkers setting */ +#define SERVER_DISABLED SERVER_NUM_STATUS +#define MOD_STATUS_NUM_STATUS (SERVER_NUM_STATUS+1) + +static char status_flags[MOD_STATUS_NUM_STATUS]; + +static int status_handler(request_rec *r) +{ + const char *loc; + apr_time_t nowtime; + apr_uint32_t up_time; + ap_loadavg_t t; + int j, i, res, written; + int ready; + int busy; + unsigned long count; + unsigned long lres, my_lres, conn_lres; + apr_off_t bytes, my_bytes, conn_bytes; + apr_off_t bcount, kbcount; + long req_time; + apr_time_t duration_global; + apr_time_t duration_slot; + int short_report; + int no_table_report; + global_score *global_record; + worker_score *ws_record; + process_score *ps_record; + char *stat_buffer; + pid_t *pid_buffer, worker_pid; + int *thread_idle_buffer = NULL; + int *thread_busy_buffer = NULL; + clock_t tu, ts, tcu, tcs; + clock_t gu, gs, gcu, gcs; + ap_generation_t mpm_generation, worker_generation; +#ifdef HAVE_TIMES + float tick; + int times_per_thread; +#endif + + if (strcmp(r->handler, STATUS_MAGIC_TYPE) && strcmp(r->handler, + "server-status")) { + return DECLINED; + } + +#ifdef HAVE_TIMES + times_per_thread = getpid() != child_pid; +#endif + + ap_mpm_query(AP_MPMQ_GENERATION, &mpm_generation); + +#ifdef HAVE_TIMES +#ifdef _SC_CLK_TCK + tick = sysconf(_SC_CLK_TCK); +#else + tick = HZ; +#endif +#endif + + ready = 0; + busy = 0; + count = 0; + bcount = 0; + kbcount = 0; + duration_global = 0; + short_report = 0; + no_table_report = 0; + + if (!ap_exists_scoreboard_image()) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01237) + "Server status unavailable in inetd mode"); + return HTTP_INTERNAL_SERVER_ERROR; + } + + pid_buffer = apr_palloc(r->pool, server_limit * sizeof(pid_t)); + stat_buffer = apr_palloc(r->pool, server_limit * thread_limit * sizeof(char)); + if (is_async) { + thread_idle_buffer = apr_palloc(r->pool, server_limit * sizeof(int)); + thread_busy_buffer = apr_palloc(r->pool, server_limit * sizeof(int)); + } + + nowtime = apr_time_now(); +#ifdef HAVE_TIMES + global_record = ap_get_scoreboard_global(); + gu = global_record->times.tms_utime; + gs = global_record->times.tms_stime; + gcu = global_record->times.tms_cutime; + gcs = global_record->times.tms_cstime; +#else + gu = gs = gcu = gcs = 0; +#endif + tu = ts = tcu = tcs = 0; + + r->allowed = (AP_METHOD_BIT << M_GET); + if (r->method_number != M_GET) + return DECLINED; + + ap_set_content_type(r, "text/html; charset=ISO-8859-1"); + + /* + * Simple table-driven form data set parser that lets you alter the header + */ + + if (r->args) { + i = 0; + while (status_options[i].id != STAT_OPT_END) { + if ((loc = ap_strstr_c(r->args, + status_options[i].form_data_str)) != NULL) { + switch (status_options[i].id) { + case STAT_OPT_REFRESH: { + apr_size_t len = strlen(status_options[i].form_data_str); + long t = 0; + + if (*(loc + len ) == '=') { + t = atol(loc + len + 1); + } + apr_table_setn(r->headers_out, + status_options[i].hdr_out_str, + apr_ltoa(r->pool, t < 1 ? 10 : t)); + break; + } + case STAT_OPT_NOTABLE: + no_table_report = 1; + break; + case STAT_OPT_AUTO: + ap_set_content_type(r, "text/plain; charset=ISO-8859-1"); + short_report = 1; + break; + } + } + + i++; + } + } + + ws_record = apr_palloc(r->pool, sizeof *ws_record); + + for (i = 0; i < server_limit; ++i) { +#ifdef HAVE_TIMES + clock_t proc_tu = 0, proc_ts = 0, proc_tcu = 0, proc_tcs = 0; + clock_t tmp_tu, tmp_ts, tmp_tcu, tmp_tcs; +#endif + + ps_record = ap_get_scoreboard_process(i); + if (is_async) { + thread_idle_buffer[i] = 0; + thread_busy_buffer[i] = 0; + } + for (j = 0; j < thread_limit; ++j) { + int indx = (i * thread_limit) + j; + + ap_copy_scoreboard_worker(ws_record, i, j); + res = ws_record->status; + + if ((i >= max_servers || j >= threads_per_child) + && (res == SERVER_DEAD)) + stat_buffer[indx] = status_flags[SERVER_DISABLED]; + else + stat_buffer[indx] = status_flags[res]; + + if (!ps_record->quiescing + && ps_record->pid) { + if (res == SERVER_READY) { + if (ps_record->generation == mpm_generation) + ready++; + if (is_async) + thread_idle_buffer[i]++; + } + else if (res != SERVER_DEAD && + res != SERVER_STARTING && + res != SERVER_IDLE_KILL) { + busy++; + if (is_async) { + if (res == SERVER_GRACEFUL) + thread_idle_buffer[i]++; + else + thread_busy_buffer[i]++; + } + } + } + + /* XXX what about the counters for quiescing/seg faulted + * processes? should they be counted or not? GLA + */ + if (ap_extended_status) { + lres = ws_record->access_count; + bytes = ws_record->bytes_served; + + if (lres != 0 || (res != SERVER_READY && res != SERVER_DEAD)) { +#ifdef HAVE_TIMES + tmp_tu = ws_record->times.tms_utime; + tmp_ts = ws_record->times.tms_stime; + tmp_tcu = ws_record->times.tms_cutime; + tmp_tcs = ws_record->times.tms_cstime; + + if (times_per_thread) { + proc_tu += tmp_tu; + proc_ts += tmp_ts; + proc_tcu += tmp_tcu; + proc_tcs += tmp_tcs; + } + else { + if (tmp_tu > proc_tu || + tmp_ts > proc_ts || + tmp_tcu > proc_tcu || + tmp_tcs > proc_tcs) { + proc_tu = tmp_tu; + proc_ts = tmp_ts; + proc_tcu = tmp_tcu; + proc_tcs = tmp_tcs; + } + } +#endif /* HAVE_TIMES */ + + count += lres; + bcount += bytes; + duration_global += ws_record->duration; + + if (bcount >= KBYTE) { + kbcount += (bcount >> 10); + bcount = bcount & 0x3ff; + } + } + } + } +#ifdef HAVE_TIMES + tu += proc_tu; + ts += proc_ts; + tcu += proc_tcu; + tcs += proc_tcs; +#endif + pid_buffer[i] = ps_record->pid; + } + + /* up_time in seconds */ + up_time = (apr_uint32_t) apr_time_sec(nowtime - + ap_scoreboard_image->global->restart_time); + ap_get_loadavg(&t); + + if (!short_report) { + ap_rputs(DOCTYPE_HTML_3_2 + "\n" + "Apache Status\n" + "\n" + "

Apache Server Status for ", r); + ap_rvputs(r, ap_escape_html(r->pool, ap_get_server_name(r)), + " (via ", r->connection->local_ip, + ")

\n\n", NULL); + ap_rvputs(r, "
Server Version: ", + ap_get_server_description(), "
\n", NULL); + ap_rvputs(r, "
Server MPM: ", + ap_show_mpm(), "
\n", NULL); + ap_rvputs(r, "
Server Built: ", + ap_get_server_built(), "\n

\n", NULL); + ap_rvputs(r, "
Current Time: ", + ap_ht_time(r->pool, nowtime, DEFAULT_TIME_FORMAT, 0), + "
\n", NULL); + ap_rvputs(r, "
Restart Time: ", + ap_ht_time(r->pool, + ap_scoreboard_image->global->restart_time, + DEFAULT_TIME_FORMAT, 0), + "
\n", NULL); + ap_rprintf(r, "
Parent Server Config. Generation: %d
\n", + ap_state_query(AP_SQ_CONFIG_GEN)); + ap_rprintf(r, "
Parent Server MPM Generation: %d
\n", + (int)mpm_generation); + ap_rputs("
Server uptime: ", r); + show_time(r, up_time); + ap_rputs("
\n", r); + ap_rprintf(r, "
Server load: %.2f %.2f %.2f
\n", + t.loadavg, t.loadavg5, t.loadavg15); + } + else { + ap_rvputs(r, ap_get_server_name(r), "\n", NULL); + ap_rvputs(r, "ServerVersion: ", + ap_get_server_description(), "\n", NULL); + ap_rvputs(r, "ServerMPM: ", + ap_show_mpm(), "\n", NULL); + ap_rvputs(r, "Server Built: ", + ap_get_server_built(), "\n", NULL); + ap_rvputs(r, "CurrentTime: ", + ap_ht_time(r->pool, nowtime, DEFAULT_TIME_FORMAT, 0), + "\n", NULL); + ap_rvputs(r, "RestartTime: ", + ap_ht_time(r->pool, + ap_scoreboard_image->global->restart_time, + DEFAULT_TIME_FORMAT, 0), + "\n", NULL); + ap_rprintf(r, "ParentServerConfigGeneration: %d\n", + ap_state_query(AP_SQ_CONFIG_GEN)); + ap_rprintf(r, "ParentServerMPMGeneration: %d\n", + (int)mpm_generation); + ap_rprintf(r, "ServerUptimeSeconds: %u\n", + up_time); + ap_rputs("ServerUptime:", r); + show_time(r, up_time); + ap_rputs("\n", r); + ap_rprintf(r, "Load1: %.2f\nLoad5: %.2f\nLoad15: %.2f\n", + t.loadavg, t.loadavg5, t.loadavg15); + } + + if (ap_extended_status) { + clock_t cpu = gu + gs + gcu + gcs + tu + ts + tcu + tcs; + if (short_report) { + ap_rprintf(r, "Total Accesses: %lu\nTotal kBytes: %" + APR_OFF_T_FMT "\nTotal Duration: %" + APR_TIME_T_FMT "\n", + count, kbcount, apr_time_as_msec(duration_global)); + +#ifdef HAVE_TIMES + /* Allow for OS/2 not having CPU stats */ + ap_rprintf(r, "CPUUser: %g\nCPUSystem: %g\nCPUChildrenUser: %g\nCPUChildrenSystem: %g\n", + (gu + tu) / tick, (gs + ts) / tick, (gcu + tcu) / tick, (gcs + tcs) / tick); + + if (cpu) + ap_rprintf(r, "CPULoad: %g\n", + cpu / tick / up_time * 100.); +#endif + + ap_rprintf(r, "Uptime: %ld\n", (long) (up_time)); + if (up_time > 0) { + ap_rprintf(r, "ReqPerSec: %g\n", + (float) count / (float) up_time); + + ap_rprintf(r, "BytesPerSec: %g\n", + KBYTE * (float) kbcount / (float) up_time); + } + if (count > 0) { + ap_rprintf(r, "BytesPerReq: %g\n", + KBYTE * (float) kbcount / (float) count); + ap_rprintf(r, "DurationPerReq: %g\n", + (float) apr_time_as_msec(duration_global) / (float) count); + } + } + else { /* !short_report */ + ap_rprintf(r, "
Total accesses: %lu - Total Traffic: ", count); + format_kbyte_out(r, kbcount); + ap_rprintf(r, " - Total Duration: %" APR_TIME_T_FMT "
\n", + apr_time_as_msec(duration_global)); + +#ifdef HAVE_TIMES + /* Allow for OS/2 not having CPU stats */ + ap_rprintf(r, "
CPU Usage: u%g s%g cu%g cs%g", + (gu + tu) / tick, (gs + ts) / tick, (gcu + tcu) / tick, (gcs + tcs) / tick); + + if (cpu) + ap_rprintf(r, " - %.3g%% CPU load
\n", + cpu / tick / up_time * 100.); + else + ap_rputs("\n", r); +#endif + + ap_rputs("
", r); + if (up_time > 0) { + ap_rprintf(r, "%.3g requests/sec - ", + (float) count / (float) up_time); + + format_byte_out(r, (unsigned long)(KBYTE * (float) kbcount + / (float) up_time)); + ap_rputs("/second", r); + } + + if (count > 0) { + if (up_time > 0) + ap_rputs(" - ", r); + format_byte_out(r, (unsigned long)(KBYTE * (float) kbcount + / (float) count)); + ap_rprintf(r, "/request - %g ms/request", + (float) apr_time_as_msec(duration_global) / (float) count); + } + + ap_rputs("
\n", r); + } /* short_report */ + } /* ap_extended_status */ + + if (!short_report) + ap_rprintf(r, "
%d requests currently being processed, " + "%d idle workers
\n", busy, ready); + else + ap_rprintf(r, "BusyWorkers: %d\nIdleWorkers: %d\n", busy, ready); + + if (!short_report) + ap_rputs("
", r); + + if (is_async) { + int write_completion = 0, lingering_close = 0, keep_alive = 0, + connections = 0, stopping = 0, procs = 0; + /* + * These differ from 'busy' and 'ready' in how gracefully finishing + * threads are counted. XXX: How to make this clear in the html? + */ + int busy_workers = 0, idle_workers = 0; + if (!short_report) + ap_rputs("\n\n\n" + "" + "" + "" + "\n" + "" + "\n" + "" + "" + "\n", r); + for (i = 0; i < server_limit; ++i) { + ps_record = ap_get_scoreboard_process(i); + if (ps_record->pid) { + connections += ps_record->connections; + write_completion += ps_record->write_completion; + keep_alive += ps_record->keep_alive; + lingering_close += ps_record->lingering_close; + busy_workers += thread_busy_buffer[i]; + idle_workers += thread_idle_buffer[i]; + procs++; + if (ps_record->quiescing) { + stopping++; + } + if (!short_report) { + const char *dying = "no"; + const char *old = ""; + if (ps_record->quiescing) { + dying = "yes"; + } + if (ps_record->generation != mpm_generation) + old = " (old gen)"; + ap_rprintf(r, "" + "" + "" + "" + "" + "\n", + i, ps_record->pid, + dying, old, + ps_record->connections, + ps_record->not_accepting ? "no" : "yes", + thread_busy_buffer[i], + thread_idle_buffer[i], + ps_record->write_completion, + ps_record->keep_alive, + ps_record->lingering_close); + } + } + } + if (!short_report) { + ap_rprintf(r, "" + "" + "" + "" + "" + "\n
SlotPIDStoppingConnectionsThreadsAsync connections
totalacceptingbusyidlewritingkeep-aliveclosing
%u%" APR_PID_T_FMT "%s%s%u%s%u%u%u%u%u
Sum%d%d%d %d%d%d%d%d
\n", + procs, stopping, + connections, + busy_workers, idle_workers, + write_completion, keep_alive, lingering_close); + } + else { + ap_rprintf(r, "Processes: %d\n" + "Stopping: %d\n" + "BusyWorkers: %d\n" + "IdleWorkers: %d\n" + "ConnsTotal: %d\n" + "ConnsAsyncWriting: %d\n" + "ConnsAsyncKeepAlive: %d\n" + "ConnsAsyncClosing: %d\n", + procs, stopping, + busy_workers, idle_workers, + connections, + write_completion, keep_alive, lingering_close); + } + } + + /* send the scoreboard 'table' out */ + if (!short_report) + ap_rputs("
", r);
+    else
+        ap_rputs("Scoreboard: ", r);
+
+    written = 0;
+    for (i = 0; i < server_limit; ++i) {
+        for (j = 0; j < thread_limit; ++j) {
+            int indx = (i * thread_limit) + j;
+            if (stat_buffer[indx] != status_flags[SERVER_DISABLED]) {
+                ap_rputc(stat_buffer[indx], r);
+                if ((written % STATUS_MAXLINE == (STATUS_MAXLINE - 1))
+                    && !short_report)
+                    ap_rputs("\n", r);
+                written++;
+            }
+        }
+    }
+
+
+    if (short_report)
+        ap_rputs("\n", r);
+    else {
+        ap_rputs("
\n" + "

Scoreboard Key:
\n" + "\"_\" Waiting for Connection, \n" + "\"S\" Starting up, \n" + "\"R\" Reading Request,
\n" + "\"W\" Sending Reply, \n" + "\"K\" Keepalive (read), \n" + "\"D\" DNS Lookup,
\n" + "\"C\" Closing connection, \n" + "\"L\" Logging, \n" + "\"G\" Gracefully finishing,
\n" + "\"I\" Idle cleanup of worker, \n" + "\".\" Open slot with no current process
\n" + "

\n", r); + if (!ap_extended_status) { + int j; + int k = 0; + ap_rputs("PID Key:
\n" + "
\n", r);
+            for (i = 0; i < server_limit; ++i) {
+                for (j = 0; j < thread_limit; ++j) {
+                    int indx = (i * thread_limit) + j;
+
+                    if (stat_buffer[indx] != '.') {
+                        ap_rprintf(r, "   %" APR_PID_T_FMT
+                                   " in state: %c ", pid_buffer[i],
+                                   stat_buffer[indx]);
+
+                        if (++k >= 3) {
+                            ap_rputs("\n", r);
+                            k = 0;
+                        } else
+                            ap_rputs(",", r);
+                    }
+                }
+            }
+
+            ap_rputs("\n"
+                     "
\n", r); + } + } + + if (ap_extended_status && !short_report) { + if (no_table_report) + ap_rputs("

Server Details

\n\n", r); + else + ap_rputs("\n\n" + "" + "" +#ifdef HAVE_TIMES + "" +#endif + "" + "" + "" + "\n\n", r); + + for (i = 0; i < server_limit; ++i) { + for (j = 0; j < thread_limit; ++j) { + ap_copy_scoreboard_worker(ws_record, i, j); + + if (ws_record->access_count == 0 && + (ws_record->status == SERVER_READY || + ws_record->status == SERVER_DEAD)) { + continue; + } + + ps_record = ap_get_scoreboard_process(i); + + if (ws_record->start_time == 0L) + req_time = 0L; + else + req_time = (long) + apr_time_as_msec(ws_record->stop_time - + ws_record->start_time); + if (req_time < 0L) + req_time = 0L; + + lres = ws_record->access_count; + my_lres = ws_record->my_access_count; + conn_lres = ws_record->conn_count; + bytes = ws_record->bytes_served; + my_bytes = ws_record->my_bytes_served; + conn_bytes = ws_record->conn_bytes; + duration_slot = ws_record->duration; + if (ws_record->pid) { /* MPM sets per-worker pid and generation */ + worker_pid = ws_record->pid; + worker_generation = ws_record->generation; + } + else { + worker_pid = ps_record->pid; + worker_generation = ps_record->generation; + } + + if (no_table_report) { + if (ws_record->status == SERVER_DEAD) + ap_rprintf(r, + "Server %d-%d (-): %d|%lu|%lu [", + i, (int)worker_generation, + (int)conn_lres, my_lres, lres); + else + ap_rprintf(r, + "Server %d-%d (%" + APR_PID_T_FMT "): %d|%lu|%lu [", + i, (int) worker_generation, + worker_pid, + (int)conn_lres, my_lres, lres); + + switch (ws_record->status) { + case SERVER_READY: + ap_rputs("Ready", r); + break; + case SERVER_STARTING: + ap_rputs("Starting", r); + break; + case SERVER_BUSY_READ: + ap_rputs("Read", r); + break; + case SERVER_BUSY_WRITE: + ap_rputs("Write", r); + break; + case SERVER_BUSY_KEEPALIVE: + ap_rputs("Keepalive", r); + break; + case SERVER_BUSY_LOG: + ap_rputs("Logging", r); + break; + case SERVER_BUSY_DNS: + ap_rputs("DNS lookup", r); + break; + case SERVER_CLOSING: + ap_rputs("Closing", r); + break; + case SERVER_DEAD: + ap_rputs("Dead", r); + break; + case SERVER_GRACEFUL: + ap_rputs("Graceful", r); + break; + case SERVER_IDLE_KILL: + ap_rputs("Dying", r); + break; + default: + ap_rputs("?STATE?", r); + break; + } + + ap_rprintf(r, "] " +#ifdef HAVE_TIMES + "u%g s%g cu%g cs%g" +#endif + "\n %ld %ld %" APR_TIME_T_FMT "(", +#ifdef HAVE_TIMES + ws_record->times.tms_utime / tick, + ws_record->times.tms_stime / tick, + ws_record->times.tms_cutime / tick, + ws_record->times.tms_cstime / tick, +#endif + (long)apr_time_sec(nowtime - + ws_record->last_used), + (long) req_time, apr_time_as_msec(duration_slot)); + + format_byte_out(r, conn_bytes); + ap_rputs("|", r); + format_byte_out(r, my_bytes); + ap_rputs("|", r); + format_byte_out(r, bytes); + ap_rputs(")\n", r); + ap_rprintf(r, + " %s {%s}(%s)[%s]
\n\n", + ap_escape_html(r->pool, + ws_record->client64), + ap_escape_html(r->pool, + ap_escape_logitem(r->pool, + ws_record->request)), + ap_escape_html(r->pool, + ws_record->protocol), + ap_escape_html(r->pool, + ws_record->vhost)); + } + else { /* !no_table_report */ + if (ws_record->status == SERVER_DEAD) + ap_rprintf(r, + "" +#ifdef HAVE_TIMES + "" +#endif + "" + "\n\n", + ap_escape_html(r->pool, + ws_record->client64), + ap_escape_html(r->pool, + ws_record->protocol), + ap_escape_html(r->pool, + ws_record->vhost), + ap_escape_html(r->pool, + ap_escape_logitem(r->pool, + ws_record->request))); + } /* no_table_report */ + } /* for (j...) */ + } /* for (i...) */ + + if (!no_table_report) { + ap_rputs("
SrvPIDAccMCPU\nSSReqDurConnChildSlotClientProtocolVHostRequest
%d-%d-%d/%lu/%lu", + i, (int)worker_generation, + (int)conn_lres, my_lres, lres); + else + ap_rprintf(r, + "
%d-%d%" + APR_PID_T_FMT + "%d/%lu/%lu", + i, (int)worker_generation, + worker_pid, + (int)conn_lres, + my_lres, lres); + + switch (ws_record->status) { + case SERVER_READY: + ap_rputs("_", r); + break; + case SERVER_STARTING: + ap_rputs("S", r); + break; + case SERVER_BUSY_READ: + ap_rputs("R", r); + break; + case SERVER_BUSY_WRITE: + ap_rputs("W", r); + break; + case SERVER_BUSY_KEEPALIVE: + ap_rputs("K", r); + break; + case SERVER_BUSY_LOG: + ap_rputs("L", r); + break; + case SERVER_BUSY_DNS: + ap_rputs("D", r); + break; + case SERVER_CLOSING: + ap_rputs("C", r); + break; + case SERVER_DEAD: + ap_rputs(".", r); + break; + case SERVER_GRACEFUL: + ap_rputs("G", r); + break; + case SERVER_IDLE_KILL: + ap_rputs("I", r); + break; + default: + ap_rputs("?", r); + break; + } + + ap_rprintf(r, + "\n%.2f%ld%ld%" APR_TIME_T_FMT, +#ifdef HAVE_TIMES + (ws_record->times.tms_utime + + ws_record->times.tms_stime + + ws_record->times.tms_cutime + + ws_record->times.tms_cstime) / tick, +#endif + (long)apr_time_sec(nowtime - + ws_record->last_used), + (long)req_time, apr_time_as_msec(duration_slot)); + + ap_rprintf(r, "%-1.1f%-2.2f%-2.2f\n", + (float)conn_bytes / KBYTE, (float) my_bytes / MBYTE, + (float)bytes / MBYTE); + + ap_rprintf(r, "%s%s%s%s
\n \ +
\ +\n \ +\n \ +\n \ +\n \ +\n" + +#ifdef HAVE_TIMES +"\n" +#endif + +"\n \ +\n \ +\n \ +\n \ +\n \ +\n \ +
SrvChild Server number - generation
PIDOS process ID
AccNumber of accesses this connection / this child / this slot
MMode of operation
CPUCPU usage, number of seconds
SSSeconds since beginning of most recent request
ReqMilliseconds required to process most recent request
DurSum of milliseconds required to process all requests
ConnKilobytes transferred this connection
ChildMegabytes transferred this child
SlotTotal megabytes transferred this slot
\n", r); + } + } /* if (ap_extended_status && !short_report) */ + else { + + if (!short_report) { + ap_rputs("
To obtain a full report with current status " + "information you need to use the " + "ExtendedStatus On directive.\n", r); + } + } + + { + /* Run extension hooks to insert extra content. */ + int flags = + (short_report ? AP_STATUS_SHORT : 0) | + (no_table_report ? AP_STATUS_NOTABLE : 0) | + (ap_extended_status ? AP_STATUS_EXTENDED : 0); + + ap_run_status_hook(r, flags); + } + + if (!short_report) { + ap_rputs(ap_psignature("
\n",r), r); + ap_rputs("\n", r); + } + + return 0; +} + +static int status_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp) +{ + /* When mod_status is loaded, default our ExtendedStatus to 'on' + * other modules which prefer verbose scoreboards may play a similar game. + * If left to their own requirements, mpm modules can make do with simple + * scoreboard entries. + */ + ap_extended_status = 1; + return OK; +} + +static int status_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, + server_rec *s) +{ + status_flags[SERVER_DEAD] = '.'; /* We don't want to assume these are in */ + status_flags[SERVER_READY] = '_'; /* any particular order in scoreboard.h */ + status_flags[SERVER_STARTING] = 'S'; + status_flags[SERVER_BUSY_READ] = 'R'; + status_flags[SERVER_BUSY_WRITE] = 'W'; + status_flags[SERVER_BUSY_KEEPALIVE] = 'K'; + status_flags[SERVER_BUSY_LOG] = 'L'; + status_flags[SERVER_BUSY_DNS] = 'D'; + status_flags[SERVER_CLOSING] = 'C'; + status_flags[SERVER_GRACEFUL] = 'G'; + status_flags[SERVER_IDLE_KILL] = 'I'; + status_flags[SERVER_DISABLED] = ' '; + ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit); + ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit); + ap_mpm_query(AP_MPMQ_MAX_THREADS, &threads_per_child); + /* work around buggy MPMs */ + if (threads_per_child == 0) + threads_per_child = 1; + ap_mpm_query(AP_MPMQ_MAX_DAEMONS, &max_servers); + ap_mpm_query(AP_MPMQ_IS_ASYNC, &is_async); + return OK; +} + +#ifdef HAVE_TIMES +static void status_child_init(apr_pool_t *p, server_rec *s) +{ + child_pid = getpid(); +} +#endif + +static void register_hooks(apr_pool_t *p) +{ + ap_hook_handler(status_handler, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_pre_config(status_pre_config, NULL, NULL, APR_HOOK_LAST); + ap_hook_post_config(status_init, NULL, NULL, APR_HOOK_MIDDLE); +#ifdef HAVE_TIMES + ap_hook_child_init(status_child_init, NULL, NULL, APR_HOOK_MIDDLE); +#endif +} + +AP_DECLARE_MODULE(status) = +{ + STANDARD20_MODULE_STUFF, + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + NULL, /* command table */ + register_hooks /* register_hooks */ +}; diff --git a/modules/generators/mod_status.dep b/modules/generators/mod_status.dep new file mode 100644 index 0000000..7ec631b --- /dev/null +++ b/modules/generators/mod_status.dep @@ -0,0 +1,60 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_status.mak + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + + +.\mod_status.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_mpm.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\scoreboard.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_script.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + ".\mod_status.h"\ + diff --git a/modules/generators/mod_status.dsp b/modules/generators/mod_status.dsp new file mode 100644 index 0000000..4596640 --- /dev/null +++ b/modules/generators/mod_status.dsp @@ -0,0 +1,111 @@ +# Microsoft Developer Studio Project File - Name="mod_status" - 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_status - 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_status.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_status.mak" CFG="mod_status - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_status - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_status - 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_status - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "STATUS_DECLARE_EXPORT" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "STATUS_DECLARE_EXPORT" /Fd"Release\mod_status_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_status.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_status.so" /d LONG_NAME="status_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:".\Release\mod_status.so" /base:@..\..\os\win32\BaseAddr.ref,mod_status.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_status.so" /base:@..\..\os\win32\BaseAddr.ref,mod_status.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_status.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_status - 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" /D "STATUS_DECLARE_EXPORT" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "STATUS_DECLARE_EXPORT" /Fd"Debug\mod_status_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_status.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_status.so" /d LONG_NAME="status_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_status.so" /base:@..\..\os\win32\BaseAddr.ref,mod_status.so +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_status.so" /base:@..\..\os\win32\BaseAddr.ref,mod_status.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_status.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_status - Win32 Release" +# Name "mod_status - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_status.c +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/generators/mod_status.exp b/modules/generators/mod_status.exp new file mode 100644 index 0000000..5438093 --- /dev/null +++ b/modules/generators/mod_status.exp @@ -0,0 +1 @@ +status_module diff --git a/modules/generators/mod_status.h b/modules/generators/mod_status.h new file mode 100644 index 0000000..721b96d --- /dev/null +++ b/modules/generators/mod_status.h @@ -0,0 +1,64 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file mod_status.h + * @brief Status Report Extension Module to Apache + * + * @defgroup MOD_STATUS mod_status + * @ingroup APACHE_MODS + * @{ + */ + +#ifndef MOD_STATUS_H +#define MOD_STATUS_H + +#include "ap_config.h" +#include "httpd.h" + +#define AP_STATUS_SHORT (0x1) /* short, non-HTML report requested */ +#define AP_STATUS_NOTABLE (0x2) /* HTML report without tables */ +#define AP_STATUS_EXTENDED (0x4) /* detailed report */ + +#if !defined(WIN32) +#define STATUS_DECLARE(type) type +#define STATUS_DECLARE_NONSTD(type) type +#define STATUS_DECLARE_DATA +#elif defined(STATUS_DECLARE_STATIC) +#define STATUS_DECLARE(type) type __stdcall +#define STATUS_DECLARE_NONSTD(type) type +#define STATUS_DECLARE_DATA +#elif defined(STATUS_DECLARE_EXPORT) +#define STATUS_DECLARE(type) __declspec(dllexport) type __stdcall +#define STATUS_DECLARE_NONSTD(type) __declspec(dllexport) type +#define STATUS_DECLARE_DATA __declspec(dllexport) +#else +#define STATUS_DECLARE(type) __declspec(dllimport) type __stdcall +#define STATUS_DECLARE_NONSTD(type) __declspec(dllimport) type +#define STATUS_DECLARE_DATA __declspec(dllimport) +#endif + +/* Optional hooks which can insert extra content into the mod_status + * output. FLAGS will be set to the bitwise OR of any of the + * AP_STATUS_* flags. + * + * Implementations of this hook should generate content using + * functions in the ap_rputs/ap_rprintf family; each hook should + * return OK or DECLINED. */ +APR_DECLARE_EXTERNAL_HOOK(ap, STATUS, int, status_hook, + (request_rec *r, int flags)) +#endif +/** @} */ diff --git a/modules/generators/mod_status.mak b/modules/generators/mod_status.mak new file mode 100644 index 0000000..168bbc8 --- /dev/null +++ b/modules/generators/mod_status.mak @@ -0,0 +1,353 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_status.dsp +!IF "$(CFG)" == "" +CFG=mod_status - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_status - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_status - Win32 Release" && "$(CFG)" != "mod_status - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_status.mak" CFG="mod_status - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_status - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_status - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_status - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_status.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_status.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_status.obj" + -@erase "$(INTDIR)\mod_status.res" + -@erase "$(INTDIR)\mod_status_src.idb" + -@erase "$(INTDIR)\mod_status_src.pdb" + -@erase "$(OUTDIR)\mod_status.exp" + -@erase "$(OUTDIR)\mod_status.lib" + -@erase "$(OUTDIR)\mod_status.pdb" + -@erase "$(OUTDIR)\mod_status.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "STATUS_DECLARE_EXPORT" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_status_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_status.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_status.so" /d LONG_NAME="status_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_status.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_status.pdb" /debug /out:"$(OUTDIR)\mod_status.so" /implib:"$(OUTDIR)\mod_status.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_status.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_status.obj" \ + "$(INTDIR)\mod_status.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" + +"$(OUTDIR)\mod_status.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_status.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_status.so" + if exist .\Release\mod_status.so.manifest mt.exe -manifest .\Release\mod_status.so.manifest -outputresource:.\Release\mod_status.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_status - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_status.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_status.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_status.obj" + -@erase "$(INTDIR)\mod_status.res" + -@erase "$(INTDIR)\mod_status_src.idb" + -@erase "$(INTDIR)\mod_status_src.pdb" + -@erase "$(OUTDIR)\mod_status.exp" + -@erase "$(OUTDIR)\mod_status.lib" + -@erase "$(OUTDIR)\mod_status.pdb" + -@erase "$(OUTDIR)\mod_status.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "STATUS_DECLARE_EXPORT" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_status_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_status.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_status.so" /d LONG_NAME="status_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_status.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_status.pdb" /debug /out:"$(OUTDIR)\mod_status.so" /implib:"$(OUTDIR)\mod_status.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_status.so +LINK32_OBJS= \ + "$(INTDIR)\mod_status.obj" \ + "$(INTDIR)\mod_status.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" + +"$(OUTDIR)\mod_status.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_status.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_status.so" + if exist .\Debug\mod_status.so.manifest mt.exe -manifest .\Debug\mod_status.so.manifest -outputresource:.\Debug\mod_status.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_status.dep") +!INCLUDE "mod_status.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_status.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_status - Win32 Release" || "$(CFG)" == "mod_status - Win32 Debug" + +!IF "$(CFG)" == "mod_status - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\generators" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\generators" + +!ELSEIF "$(CFG)" == "mod_status - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\generators" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\generators" + +!ENDIF + +!IF "$(CFG)" == "mod_status - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\generators" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\generators" + +!ELSEIF "$(CFG)" == "mod_status - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\generators" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\generators" + +!ENDIF + +!IF "$(CFG)" == "mod_status - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\generators" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\generators" + +!ELSEIF "$(CFG)" == "mod_status - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\generators" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\generators" + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_status - Win32 Release" + + +"$(INTDIR)\mod_status.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_status.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_status.so" /d LONG_NAME="status_module for Apache" $(SOURCE) + + +!ELSEIF "$(CFG)" == "mod_status - Win32 Debug" + + +"$(INTDIR)\mod_status.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_status.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_status.so" /d LONG_NAME="status_module for Apache" $(SOURCE) + + +!ENDIF + +SOURCE=.\mod_status.c + +"$(INTDIR)\mod_status.obj" : $(SOURCE) "$(INTDIR)" + + + +!ENDIF + diff --git a/modules/generators/mod_suexec.c b/modules/generators/mod_suexec.c new file mode 100644 index 0000000..a71b0a8 --- /dev/null +++ b/modules/generators/mod_suexec.c @@ -0,0 +1,139 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_request.h" +#include "apr_strings.h" +#include "unixd.h" +#include "mpm_common.h" +#include "mod_suexec.h" + +module AP_MODULE_DECLARE_DATA suexec_module; + +/* + * Create a configuration specific to this module for a server or directory + * location, and fill it with the default settings. + */ +static void *mkconfig(apr_pool_t *p) +{ + suexec_config_t *cfg = apr_palloc(p, sizeof(suexec_config_t)); + + cfg->active = 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); +} + +static const char *set_suexec_ugid(cmd_parms *cmd, void *mconfig, + const char *uid, const char *gid) +{ + suexec_config_t *cfg = (suexec_config_t *) mconfig; + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_CONTEXT); + + if (err != NULL) { + return err; + } + + if (!ap_unixd_config.suexec_enabled) { + return apr_pstrcat(cmd->pool, "SuexecUserGroup configured, but " + "suEXEC is disabled: ", + ap_unixd_config.suexec_disabled_reason, NULL); + } + + cfg->ugid.uid = ap_uname2id(uid); + cfg->ugid.gid = ap_gname2id(gid); + cfg->ugid.userdir = 0; + cfg->active = 1; + + return NULL; +} + +static ap_unix_identity_t *get_suexec_id_doer(const request_rec *r) +{ + suexec_config_t *cfg = + (suexec_config_t *) ap_get_module_config(r->per_dir_config, &suexec_module); + + return cfg->active ? &cfg->ugid : NULL; +} + +#define SUEXEC_POST_CONFIG_USERDATA "suexec_post_config_userdata" +static int suexec_post_config(apr_pool_t *p, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *s) +{ + void *reported; + + apr_pool_userdata_get(&reported, SUEXEC_POST_CONFIG_USERDATA, + s->process->pool); + + if ((reported == NULL) && ap_unixd_config.suexec_enabled) { + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(01232) + "suEXEC mechanism enabled (wrapper: %s)", SUEXEC_BIN); + + apr_pool_userdata_set((void *)1, SUEXEC_POST_CONFIG_USERDATA, + apr_pool_cleanup_null, s->process->pool); + } + + return OK; +} +#undef SUEXEC_POST_CONFIG_USERDATA + +/* + * Define the directives specific to this module. This structure is referenced + * later by the 'module' structure. + */ +static const command_rec suexec_cmds[] = +{ + /* XXX - Another important reason not to allow this in .htaccess is that + * the ap_[ug]name2id() is not thread-safe */ + AP_INIT_TAKE2("SuexecUserGroup", set_suexec_ugid, NULL, RSRC_CONF, + "User and group for spawned processes"), + { NULL } +}; + +static void suexec_hooks(apr_pool_t *p) +{ + ap_hook_get_suexec_identity(get_suexec_id_doer,NULL,NULL,APR_HOOK_MIDDLE); + ap_hook_post_config(suexec_post_config,NULL,NULL,APR_HOOK_MIDDLE); +} + +AP_DECLARE_MODULE(suexec) = +{ + 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 */ + suexec_cmds, /* command table */ + suexec_hooks /* register hooks */ +}; diff --git a/modules/generators/mod_suexec.h b/modules/generators/mod_suexec.h new file mode 100644 index 0000000..80e2504 --- /dev/null +++ b/modules/generators/mod_suexec.h @@ -0,0 +1,33 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file mod_suexec.h + * @brief SuExec Extension Module for Apache + * + * @defgroup MOD_SUEXEC mod_suexec + * @ingroup APACHE_MODS + * @{ + */ + +#include "unixd.h" + +typedef struct { + ap_unix_identity_t ugid; + int active; +} suexec_config_t; + +/** @}*/ -- cgit v1.2.3